Using a key function to add an element to XML does not work using xslt

I am trying to work with a "key" function in XSLT to extract a value and add it to a specific position in XML. But I do not understand. Below is the input and XSLT.

Please, help.

XML input:

<input xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xsi:noNamespaceSchemaLocation="nbA2.8.90.xsd"> <info>User Info </info> <team>User Team </team> <nodeA id="test"> <inodeAA> Sample </inodeAA> <inodeAB> Samples </inodeAB> </nodeA> <nodeA id="test2"> <inodeAA> Sample </inodeAA> <inodeAB> Samples </inodeAB> </nodeA> <ns3:output xmlns:ns3="http://mysample.org"> <ns3:reply id="test"> <ns3:pkey>55555</ns3:pkey> <ns3:place>SampleLoc</ns3:place> </ns3:reply> <ns3:reply id="test2"> <ns3:pkey>55557</ns3:pkey> <ns3:place>SampleLoc2</ns3:place> </ns3:reply> </ns3:output> </input> 

Expected Result:

 <input xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xsi:noNamespaceSchemaLocation="nbA2.8.90.xsd"> <info>User Info </info> <team>User Team </team> <nodeA id="test"> <inodeAA> Sample </inodeAA> <pkey>55555</pkey> <inodeAB> Samples </inodeAB> </nodeA> <nodeA id="test2"> <inodeAA> Sample </inodeAA> <pkey>55557</pkey> <inodeAB> Samples </inodeAB> </nodeA> </input> 

Given below is my XSL:

  <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:ns3="http://mysample.org" exclude-result-prefixes="soap xsl"> <xsl:output omit-xml-declaration="no" indent="yes" /> <xsl:key name="parKey" match="ns3:output/ns3:reply/ns3:pkey" use="/input/ns3:output/ns3:reply/@id"></xsl:key> <xsl:template match="@*|node()"> <xsl:copy copy-namespaces="no"> <xsl:apply-templates select="@*|node()" /> </xsl:copy> </xsl:template> <xsl:template match="/input"> <xsl:variable name="output_186" select="ns3:output"/> <xsl:copy> <xsl:copy-of copy-namespaces="no" select="./@*" /> <xsl:copy-of select=" * except $output_186"></xsl:copy-of> </xsl:copy> </xsl:template> <xsl:template match="/input//nodeA"> <xsl:copy> <xsl:copy-of copy-namespaces="no" select="./@*" /> <pkey> <xsl:copy-of select="key('parKey', @id)|." /> </pkey> <xsl:copy-of copy-namespaces="no" select="node()" /> </xsl:copy> </xsl:template> </xsl:stylesheet> 

The output is as follows: Which does not show my required field

  <input xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xsi:noNamespaceSchemaLocation="nbA2.8.90.xsd" > <info>User Info </info> <team>User Team </team> <nodeA id="test"> <inodeAA> Sample </inodeAA> <inodeAB> Samples </inodeAB> </nodeA> <nodeA id="test2"> <inodeAA> Sample </inodeAA> <inodeAB> Samples </inodeAB> </nodeA> </input> 

Help me where exactly I am missing.

Thanks...

+4
source share
3 answers

Shorter solution :

 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ns3="http://mysample.org"> <xsl:output omit-xml-declaration="yes" indent="yes"/> <xsl:strip-space elements="*"/> <xsl:key name="kpKeyByParentId" match="ns3:pkey" use="../@id"/> <xsl:template match="node()|@*" name="identity"> <xsl:copy> <xsl:apply-templates select="node()|@*"/> </xsl:copy> </xsl:template> <xsl:template match="inodeAA"> <xsl:call-template name="identity"/> <xsl:apply-templates select="key('kpKeyByParentId', ../@id)"/> </xsl:template> <xsl:template match="ns3:pkey"> <xsl:element name="{local-name()}"> <xsl:apply-templates/> </xsl:element> </xsl:template> <xsl:template match="ns3:output"/> </xsl:stylesheet> 

When this conversion is applied to the provided XML document:

 <input xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xsi:noNamespaceSchemaLocation="nbA2.8.90.xsd"> <info>User Info </info> <team>User Team </team> <nodeA id="test"> <inodeAA> Sample </inodeAA> <inodeAB> Samples </inodeAB> </nodeA> <nodeA id="test2"> <inodeAA> Sample </inodeAA> <inodeAB> Samples </inodeAB> </nodeA> <ns3:output xmlns:ns3="http://mysample.org"> <ns3:reply id="test"> <ns3:pkey>55555</ns3:pkey> <ns3:place>SampleLoc</ns3:place> </ns3:reply> <ns3:reply id="test2"> <ns3:pkey>55557</ns3:pkey> <ns3:place>SampleLoc2</ns3:place> </ns3:reply> </ns3:output> </input> 

the desired, correct result is output:

 <input xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xsi:noNamespaceSchemaLocation="nbA2.8.90.xsd"> <info>User Info </info> <team>User Team </team> <nodeA id="test"> <inodeAA> Sample </inodeAA> <pkey>55555</pkey> <inodeAB> Samples </inodeAB> </nodeA> <nodeA id="test2"> <inodeAA> Sample </inodeAA> <pkey>55557</pkey> <inodeAB> Samples </inodeAB> </nodeA> </input> 

Explanation

  • An identity rule copies each node for which it is selected to run as-is.

  • The ovveriding template, which corresponds to inodeAA , calls the identification template to copy it, then applies the templates for any ns3:pkey , the parent id attribute has the same value as the id attribute of this inodeAA parent. For convenience and efficiency, this is done by calling the key() function, which refers to the key "kpKeyByParentId" , which defines ns3:pkey as a function of the id attribute of its parent.

  • The template that matches the ns3:pkey element creates an element (without a namespace) whose name is the local name of the matching element, and also copies its contents.

  • The ns3:output element and its whole subtree are excluded from processing ("deleted") by the matching template, which has an empty body.

+1
source

The most obvious is the incorrect use statement in your xsl: key. It should always be a relative path expression with respect to the node matching the match pattern. I have not developed what it should be, because I have not really developed what you are trying to do. But I suspect it should be

 match="ns3:reply" use="@id" 
+1
source

The use xsl:key attribute is the path relative to the matched element, so you probably want to

 <xsl:key name="parKey" match="ns3:pkey" use="../@id"/> 

to group pkey elements using the id attribute of their parent reply element.

But overall, your /input pattern is suspicious:

  <xsl:template match="/input"> <xsl:variable name="output_186" select="ns3:output"/> <xsl:copy> <xsl:copy-of copy-namespaces="no" select="./@*" /> <xsl:copy-of select=" * except $output_186"></xsl:copy-of> </xsl:copy> </xsl:template> 

This is just copying the entire source input element (minus ns3:output ), it does not apply templates recursively, so the nodeA template that deals with pkey will never fire.

Try something else like this:

 <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:ns3="http://mysample.org" exclude-result-prefixes="xsl"> <xsl:strip-space elements="*" /> <xsl:output omit-xml-declaration="no" indent="yes" /> <xsl:key name="parKey" match="ns3:pkey" use="../@id"/> <!-- copy everything --> <xsl:template match="@*|node()"> <xsl:copy copy-namespaces="no"> <xsl:apply-templates select="@*|node()" /> </xsl:copy> </xsl:template> <!-- except ns3:output --> <xsl:template match="ns3:output" /> <xsl:template match="nodeA"> <xsl:copy copy-namespaces="no"> <xsl:apply-templates select="@*|node()" /> <xsl:element name="pkey"> <xsl:value-of select="key('parKey', @id)" /> </xsl:element> </xsl:copy> </xsl:template> </xsl:stylesheet> 
+1
source

All Articles