开发者

Copying whole nodes based on single sub-value in XSLT

开发者 https://www.devze.com 2023-01-30 13:51 出处:网络
I have two files, and want to combine/overwrite whole nodes based on a particular matching value in a node tree.

I have two files, and want to combine/overwrite whole nodes based on a particular matching value in a node tree.

file1.xml

  <record>
    <key>key1</key>
    <nodel>
      <field1>source field1</field1>
      <field2>source field2</field2>
      <searchkey>myname</searchkey>
    </nodel>
  </record>

file2.xml

  <record>
    <key>key2</key>
    <nodel>
      <field1>match field1</field1>
      <field2>match field2</field2>
      <searchkey>myname</searchkey>
    </nodel>
  </record>

Desired output:

  <record>
    <key>key1</key>
    <nodel>
      <field1>source field1</field1>
      <field2>source field2</field2>
      <searchkey>myname</searchkey>
    </nodel>

    <nodel>
      <field1>match field1</field1>
      <field2>match field2</field2>
      <searchkey>myname</searchkey>
    </nodel>
  </record>

So, I want to check for a match in the searchkey, between the two files, and, if it matches, copy the whole no开发者_高级运维de of the match to the output file, but keep the original key, not the match one. I will then want to delete the original nodel, though am happy to do that as a later transform.

I did ask a recent similar question for matching on the <key> nodes, but couldn't figure out how to modify it to achieve this desired result.

Thanks.


A solution with keys:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:key name="kNodeBySearchkey" match="*[searchkey]" use="searchkey"/>
    <xsl:param name="pSource2" select="'file2.xml'"/>
    <xsl:variable name="vSource2" select="document($pSource2,/)"/>
    <xsl:template match="node()|@*" name="identity">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="*[searchkey]">
        <xsl:variable name="vKey" select="searchkey"/>
        <xsl:call-template name="identity"/>
        <xsl:for-each select="$vSource2">
            <xsl:for-each select="key('kNodeBySearchkey',$vKey)">
                <xsl:call-template name="identity"/>
            </xsl:for-each>
        </xsl:for-each>
    </xsl:template>
</xsl:stylesheet>

Output:

<record>
    <key>key1</key>
    <nodel>
        <field1>source field1</field1>
        <field2>source field2</field2>
        <searchkey>myname</searchkey>
    </nodel>
    <nodel>
        <field1>source field1</field1>
        <field2>source field2</field2>
        <searchkey>myname</searchkey>
    </nodel>
</record>

Note: I you don't want to preserve the original nodel, just strip away the first xsl:call-template instruction.


This transformation:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:my="my:my">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <my:file2>
  <record>
    <key>key2</key>
    <nodel>
      <field1>match field1</field1>
      <field2>match field2</field2>
      <searchkey>myname</searchkey>
    </nodel>
  </record>
 </my:file2>

 <xsl:variable name="vFile2"
      select="document('')/*/my:file2/*"/>

 <xsl:template match="node()|@*" name="identity">
     <xsl:copy>
       <xsl:apply-templates select="node()|@*"/>
     </xsl:copy>
 </xsl:template>

 <xsl:template match="/*/*[starts-with(name(),'node')]">
  <xsl:call-template name="identity"/>
  <xsl:apply-templates mode="copy" select=
   "$vFile2/*[starts-with(name(),'node')]
                  [searchkey=current()/searchkey]
   "/>
 </xsl:template>

 <xsl:template match="node()" mode="copy">
  <xsl:call-template name="identity"/>
 </xsl:template>
</xsl:stylesheet>

when applied on the provided first XML document:

<record>
    <key>key1</key>
    <nodel>
        <field1>source field1</field1>
        <field2>source field2</field2>
        <searchkey>myname</searchkey>
    </nodel>
</record>

produces the wanted, correct result:

<record>
   <key>key1</key>
   <nodel>
      <field1>source field1</field1>
      <field2>source field2</field2>
      <searchkey>myname</searchkey>
   </nodel>
   <nodel>
      <field1>match field1</field1>
      <field2>match field2</field2>
      <searchkey>myname</searchkey>
   </nodel>
</record>

Note: The second XML document is represented inline in the stylesheet -- this is only for convenience. In practice, it will reside in its own file and only the definition of $vFile2 will be changed to:

 <xsl:variable name="vFile2"
      select="document('someFileURIHere')/*"/>


Attempt at the filename input:

xsl

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>
    <xsl:param name="pSource2" select="'file2.xml'"/>
    <xsl:variable name="vFile2" select="document($pSource2)/*"/>

 <xsl:template match="node()|@*" name="identity">
     <xsl:copy>
       <xsl:apply-templates select="node()|@*"/>
     </xsl:copy>
 </xsl:template>

 <xsl:template match="/*/*[starts-with(name(),'node')]">
  <xsl:call-template name="identity"/>
  <xsl:apply-templates select=
   "$vFile2/*[starts-with(name(),'node')]
                  [searchkey=current()/searchkey]
   "/>
 </xsl:template>
</xsl:stylesheet>

Gives

<record>
<key>key1</key>
<nodel>
<field1>source field1</field1>
<field2>source field2</field2>
<searchkey>myname</searchkey>
</nodel>
<nodel>
<field1>match field1</field1>
<field2>match field2</field2>
<searchkey>myname</searchkey>
</nodel>

With the second node repeated until it gives up.

0

精彩评论

暂无评论...
验证码 换一张
取 消