2013-02-26 84 views
-1

我正在处理一个需要复制和更新以传递进行进一步处理的xml。我遇到的问题是我没有想出一个有效的方法来做到这一点。实质上,我想有条件地更新一些数据,然后复制所有未更新的节点。为什么这是具有挑战性的,是由于要复制的节点的数量和名称的数量和方差。我也不想复制没有文本值的节点。下面是一个例子:使用XSLT有选择地复制和更新xml节点

INPUT XML

<root> 
    <PersonProfile xmlns:'namespace'> 
     <ID>0001</ID> 
     <Name> 
      <FirstName>Jonathan</FirstName> 
      <PreferredName>John</PreferredName> 
      <MiddleName>A</MiddleName> 
      <LastName>Doe</LastName> 
     </Name> 
     <Country>US</Country> 
     <Biirthdate>01-01-1980</Birthdate> 
     <BirthPlace> 
      <City>Townsville</City> 
      <State>OR</State> 
      <Country>US</Country> 
     </Birthplace> 
     <Gender>Male</Gender> 
     <HomeState>OR</HomeState> 
     ... 
     <nodeN>text</nodeN> 
    </PersonProfile> 
</root> 

的“PersonProfile”节点仅仅是“根”元素内的几个节点集,每个都有自己的数据的子集的一个。比如邮寄地址,紧急联系信息等等。我试图做的是更新节点,如果变量有一个新的值,那么复制所有未更新的节点。

这里是我当前的XSLT

<xsl:stylesheet version="2.0" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 

<xsl:variable name='updateData' select='document("report")'/> 

<!-- Identity Transform --> 
<xsl:template match='@* | node()'> 
    <xsl:if test'. != ""'> 
     <xsl:copy> 
      <xsl:apply-templates select='@* | node()'/> 
     </xsl:copy> 
     </xsl:if> 
    </xsl:template> 

<!-- Template to update Person Profile --> 
    <xsl:template match='PersonProfile'> 
    <xsl:copy> 
     <xsl:apply-templates select='*'/>  
     <xsl:element name='Name'> 
      <xsl:if test='exists($updateData/Preferred)'> 
       <xsl:element name='FirstName'> 
        <xsl:value-of select='$reportData/FirstName'/> 
       </xsl:element> 
      </xsl:if>    
      <xsl:if test='exists($updateData/Preferred)'> 
       <xsl:element name='PreferredName'> 
        <xsl:value-of select='$updateData/Preferred'/> 
       </xsl:element> 
      </xsl:if> 
      <xsl:if test='exists($updateData/Middle)'> 
      <xsl:element name='MiddleName'> 
       <xsl:value-of select='$updateData/Middle'/> 
      </xsl:element> 
      </xsl:if> 
      <xsl:if test='exists($updateData/LastName)'> 
       <xsl:element name='LastName'> 
        <xsl:value-of select='$updateData/wd:LastName'/> 
       </xsl:element> 
      </xsl:if> 
     </xsl:element> 
     <xsl:if test='exists($updateData/Country)'> 
      <xsl:element name='Country'> 
       <xsl:value-of select='$updateData/Country'/> 
      </xsl:element> 
     </xsl:if> 
     .... 
     <!-- follows same structure until end of template --> 
    </xsl:copy> 
    </xsl:template> 

<!-- More Templates to Update other Node sets --> 

</xsl:stylesheet> 

发生了什么事,现在,是它的复制所有的节点,然后添加更新的值。使用撒克逊PE 9.3.0.5,我会得到类似这样的输出:

样本输出

<root> 
    <PersonProfile xmlns:'namespace'> 
     <ID>0001</ID> 
     <Name> 
      <FirstName>Jonathan</FirstName> 
      <PreferredName>John</PreferredName> 
      <MiddleName>A</MiddleName> 
      <LastName>Doe</LastName> 
     </Name> 
     <Country>US</Country> 
     <Biirthdate>01-01-1980</Birthdate> 
     <BirthPlace> 
      <City>Townsville</City> 
      <State>OR</State> 
      <Country>US</Country> 
     </Birthplace> 
     <Gender>Male</Gender> 
     <HomeState>OR</HomeState> 
     ... 
     <nodeN>text</nodeN> 
     <PreferredName>Jonathan</PreferredName> 
     <HomeState>WA</HomeState> 
    </PersonProfile> 
</root> 

我意识到这种情况正在发生,因为我在PersonProfile应用模板到所有节点并且我可以指定排除哪些节点,但是我觉得这是一个非常糟糕的解决方案,因为节点的体积可能超过30个或更多,并且需要每个节点的书面值。我相信XML比明确列出每个节点更优雅。我想有一个出这样的:

所需的输出

<root> 
    <PersonProfile xmlns:'namespace'> 
     <ID>0001</ID> 
     <Name> 
      <FirstName>Jonathan</FirstName> 
      <PreferredName>Jonathan</PreferredName> 
      <MiddleName>A</MiddleName> 
      <LastName>Doe</LastName> 
     </Name> 
     <Country>US</Country> 
     <Biirthdate>01-01-1980</Birthdate> 
     <BirthPlace> 
      <City>Townsville</City> 
      <State>OR</State> 
      <Country>US</Country> 
     </Birthplace> 
     <Gender>Male</Gender> 
     <HomeState>WA</HomeState> 
     ... 
     <nodeN>text</nodeN> 
    </PersonProfile> 
</root> 

如果有人可以帮助我建立一个模板结构,将在XML结构的工作,我将不胜感激。它需要是“可重用的”,因为有类似的节点结构,比如我需要应用它的Person Profile,但节点名称和元素数量不同,等等。

  • Ĵ
+0

所以我找到了一个潜在的解决方案,基本上我为每个xpath创建了一个模板,可以更新然后应用它们 - 哪个工作得很好。我只有1个问题 - 当路径不存在时如何使用模板!可以说这个人缺少主页状态 - 我无法匹配做,所以我怎么能告诉模板来建立新的节点? 谢谢! – 2013-02-26 03:10:04

回答

1

这应该为你原来的问题工作:

<xsl:stylesheet version="2.0" 
       xmlns:xs="http://www.w3.org/2001/XMLSchema" 
       xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 

    <xsl:variable name='updateData' select='document("report")'/> 

    <!-- Identity Transform --> 
    <xsl:template match='@* | node()' name='copy'> 
     <xsl:copy> 
     <xsl:apply-templates select='@* | node()'/> 
     </xsl:copy> 
    </xsl:template> 

    <xsl:template match='*[not(*)]'> 
    <xsl:variable name='matchingValue' 
        select='$updateData/*[name() = name(current())]'/> 
    <xsl:choose> 
     <xsl:when test='$matchingValue'> 
     <xsl:copy> 
      <xsl:apply-templates select='@*' /> 
      <xsl:value-of select='$matchingValue'/> 
     </xsl:copy> 
     </xsl:when> 
     <xsl:when test='normalize-space()'> 
     <xsl:call-template name='copy' /> 
     </xsl:when> 
    </xsl:choose> 
    </xsl:template> 
</xsl:stylesheet> 

至于将不存在源XML,这是棘手的新元素。你能为此打开一个单独的问题吗?我可能对如何解决这个问题有一些想法。

+0

谢谢!我会马上测试一下。至于我的第二个问题,我创建了这个帖子。 http://stackoverflow.com/questions/15081758/insert-xpath-during-copy-using-apply-templates 再次感谢您提供的任何帮助! – 2013-02-26 05:08:58

+0

虽然这对我不起作用,但我仍然接受了答案,因为它在1假设下是正确的 - updateData变量中的xpath将与源xml中的元素名称匹配。我没有具体说明情况并非总是如此。我想我仍然可以修改这个工作,但只是为了澄清源xpath /元素名称与updateData变量中的数据的xpath不匹配。再次感谢您的答案! – 2013-02-26 14:02:13