Copying whole nodes based on single sub-value in XSLT
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.
精彩评论