使用 XSLT 有选择地复制和更新 xml 节点 [英] Selectively copy and update xml nodes using XSLT

查看:29
本文介绍了使用 XSLT 有选择地复制和更新 xml 节点的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm working with an xml that I need to copy and update to pass on for further processing. The issue I'm having is that I have not figured out an efficient method to do this. Essentially, I want to update some data, conditionally, then copy all the nodes that were not updated. Why this is challenging is due to the volume and variance in the number and name of nodes to be copied. I also want to NOT copy nodes that have no text value. Here is an example:

输入 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"节点只是根"元素中的几个节点集之一,每个节点集都有自己的数据子集.例如邮寄地址、紧急联系信息等.我试图做的是更新节点,如果变量有一个新值,然后复制所有未更新的节点.

The "PersonProfile" node is just one of several node sets within the "root" element, each with their own subset of data. Such as mailing address, emergency contact info, etc. What I am attempting to do is update nodes if the variable has a new value for them then copy all the nodes that were not updated.

这是我当前的 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,我会得到类似这样的输出:

What's happening right now, is that it's copying ALL the nodes and then adding the updates values. Using Saxon-PE 9.3.0.5, I'll get an output similar to this:

样本输出

  <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 有一个比明确列出每个节点更优雅的解决方案.我想要一个这样的结果:

I realize this is happening because I am applying the templates to all the nodes in PersonProfile and that I could specify which nodes to exclude, but I feel like this is a very poor solution as the volume of nodes could be upwards of 30 or more and that would require a written value for each one. I trust XML has a more elegant solution than to explicitly list each of these nodes. I would like to have an out like this:

期望的输出

  <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,但节点名称和元素数量等不同.

If anyone could help me create a template structure that would work for the xml structure, I would GREATLY appreciate it. It would need to be "reusable" as there are similar node structures like Person Profile I would have to apply it to, but with different node names and number of elements, etc.

提前感谢您的帮助!

  • J

推荐答案

这应该适用于您的原始问题:

This should work for your original question:

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

As far as inserting new elements that are not present in the source XML, that's trickier. Could you open a separate question for that? I may have some ideas for how to approach that.

这篇关于使用 XSLT 有选择地复制和更新 xml 节点的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆