(XSLT, code optimization) How do I output nodes that reference the value of sibling-nodes ..?

I am converting XML to XML using XSLT. The goal is to read the value of the <node1> , if it is null, then it should be assigned the value <node2> , if incase <node2> is also null, then the default text should be assigned .. for both tags.
EDIT: If <node2> is null and <node1> not ... then the code should not update <node2> text 'Default' , but it should be converted as is ..

This is the test XML I'm trying with:

 <root> <node1></node1> <node2></node2> <parent> <node1>data1</node1> <node2></node2> </parent> <parent> <node1></node1> <node2>data2</node2> </parent> <parent> <node1>data1</node1> <node2>data2</node2> </parent> </root> 

And this XSLT code that I developed:

  <xsl:template match="@* | node()"> <xsl:copy> <xsl:apply-templates select="@* | node()"/> </xsl:copy> </xsl:template> <xsl:template name="template1" match="node2[(following-sibling::node1[.='']|preceding-sibling::node1[.=''])]"> <xsl:choose> <xsl:when test=".=''"> <node1><xsl:text>Default</xsl:text></node1> <node2><xsl:text>Default</xsl:text></node2> </xsl:when> <xsl:otherwise> <node1> <xsl:value-of select="text()"/> </node1> <xsl:copy> <xsl:apply-templates select="node()"/> </xsl:copy> </xsl:otherwise> </xsl:choose> </xsl:template> <xsl:template name="template2" match="node1[.='']"/> 

Although my code works, I am not happy with its bulkiness of code. Is there a way to get rid of extra lines (if any) .... And is there an alternative to using 2 templates for this (namely template1 and template2), is it possible to reduce the number of templates?

+4
source share
4 answers

I. XSLT 1.0 Solution:

 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" > <xsl:output omit-xml-declaration="yes" indent="yes"/> <xsl:variable name="vReplacement">Default</xsl:variable> <xsl:variable name="vRep" select= "document('')/*/xsl:variable[@name='vReplacement']/text()"/> <xsl:template match="node()|@*"> <xsl:copy> <xsl:apply-templates select="node()|@*"/> </xsl:copy> </xsl:template> <xsl:template match="node1[not(node())] | node2[../node1[not(node())]]"> <xsl:copy> <xsl:copy-of select="../node2/text() | $vRep[not(current()/../node2/text())]"/> </xsl:copy> </xsl:template> </xsl:stylesheet> 

It is shorter and simpler than the current solutions - 7 lines less and , more importantly, one template less than the currently selected solution.

Even more important , this solution is fully declarative and push-style - without calling the named templates, and only <xsl:apply-templates> is in the identification rule.

II. XSLT 2.0 Solution

 <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" > <xsl:output omit-xml-declaration="yes" indent="yes"/> <xsl:template match="node()|@*"> <xsl:copy> <xsl:apply-templates select="node()|@*"/> </xsl:copy> </xsl:template> <xsl:template match="node1[not(node())] | node2[../node1[not(node())]]"> <xsl:copy> <xsl:sequence select="(../node2/text(), 'Default')[1]"/> </xsl:copy> </xsl:template> </xsl:stylesheet> 

Using the power of XPath 2.0 sequences, this solution is rather shorter than the XSLT 1.0 solution .

Something like this is not possible in XSLT 1.0 (for example, selecting the first one from the union of two nodes without specifying predicates to make the two nodes mutually exclusive), since a node with a default value of text and nodes node1 / node2 belong to different documents and, as we know , node ordering between nodes of different documents is implementation specific and not guaranteed / prescribed.

This solution is completely declarative (not if / then / else) and completely push: the identification rule uses only <xsl:apply-templates> .

+3
source
 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" > <xsl:template match="node() | @*"> <xsl:copy> <xsl:apply-templates select="node() | @*" /> </xsl:copy> </xsl:template> <xsl:template match="node1[.=''] | node2[.='']"> <xsl:copy> <xsl:call-template name="GetOwnValue" /> </xsl:copy> </xsl:template> <xsl:template name="GetOwnValue"> <xsl:variable name="node2" select="following-sibling::node2[1]" /> <xsl:choose> <xsl:when test="$node2 != ''"> <xsl:value-of select="$node2" /> </xsl:when> <xsl:otherwise> <xsl:text>Default</xsl:text> </xsl:otherwise> </xsl:choose> </xsl:template> </xsl:stylesheet> 
+2
source

I modified Tomalak's answer and fulfilled this requirement.
As I mentioned in my question, this code skips node2 as null (if it is null) if the sibling1 node is not null (and also if there is no sibling1 node).

This code finally became an alternative to the one I posted in my Q .. (I’m not saying that this is good enough .. but I'm glad I could try .. :-)
And this code is more efficient than mine for 10-20 ms .. :-)

Here it is.

  <xsl:template match="node() | @*"> <xsl:copy> <xsl:apply-templates select="node() | @*" /> </xsl:copy> </xsl:template> <xsl:template match="node1[.=''] | node2[.='']"> <xsl:copy> <xsl:call-template name="GetOwnValue"> <xsl:with-param name="node"> <xsl:value-of select="name()"/> </xsl:with-param> </xsl:call-template> </xsl:copy> </xsl:template> <xsl:template name="GetOwnValue"> <xsl:param name="node"/> <xsl:variable name="node2" select="following-sibling::node2[1]|preceding-sibling::node2[1]" /> <xsl:variable name="node1" select="following-sibling::node1[1]|preceding-sibling::node1[1]" /> <xsl:choose> <xsl:when test="$node2 != ''"> <xsl:value-of select="$node2" /> </xsl:when> <xsl:when test="$node!='node1' and ($node1!='' or not(following-sibling::node1[1]|preceding-sibling::node1[1]))"/> <xsl:otherwise> <xsl:text>Default</xsl:text> </xsl:otherwise> </xsl:choose> </xsl:template> 
+2
source

Using XSLT 2.0, I would do it, but yours is easier to read anyway.

 <xsl:template match="node1[.='']"> <xsl:copy> <xsl:value-of select="if (following-sibling::node2[.!='']) then following-sibling::node2[.!=''] else if (preceding-sibling::node2[.!='']) then preceding-sibling::node2[.!=''] else 'Default'"/> </xsl:copy> </xsl:template> <xsl:template match="node2[.='']"> <xsl:copy> <xsl:value-of select="if (following-sibling::node1[.!='']) then '' else if (preceding-sibling::node1[.!='']) then '' else 'Default'"/> </xsl:copy> </xsl:template> 
0
source

Source: https://habr.com/ru/post/1301676/


All Articles