【问题标题】:Selectively copy and update xml nodes using XSLT使用 XSLT 有选择地复制和更新 xml 节点
【发布时间】:2013-02-11 07:58:57
【问题描述】:

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

输入 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>

现在发生的是,它正在复制所有节点,然后添加更新值。使用 Saxon-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,但节点名称和元素数量等不同。

提前感谢您的帮助!

  • J

【问题讨论】:

  • 所以我找到了一个潜在的解决方案,基本上我正在为每个可以更新然后应用它们的 xpath 创建一个模板 - 这工作得很好。我只有 1 个问题 - 当路径不存在时如何使用模板!假设这个人缺少家乡 - 我无法匹配,那么我怎么能告诉模板构建新节点?谢谢!

标签: templates xslt replace copy nodes


【解决方案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 中不存在的新元素而言,这比较棘手。你能为此打开一个单独的问题吗?我可能对如何解决这个问题有一些想法。

【讨论】:

  • 谢谢!我很快就会对此进行测试。至于我的第二个问题,我创建了这篇文章。 stackoverflow.com/questions/15081758/… 再次感谢您提供的任何帮助!
  • 虽然这对我不起作用,但我仍然接受了答案,因为它在 1 个假设下是正确的 - updateData 变量中的 xpath 将匹配源 xml 中的元素名称。我没有具体说明情况并非总是如此。我想我仍然可以修改它以使其工作,但只是为了澄清源 xpath/元素名称与 updateData 变量中数据的 xpath 不匹配。再次感谢您的回答!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-02-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多