English 中文(简体)
(XSLT, Code optimization) How to output the nodes refering to the value of sibling-nodes ..?
原标题:

I am transforming XML to XML using XSLT, The objective is to read the value of tag <node1>, if it is null then it must be assigned with the value of <node2>, if incase <node2>, is also null, then default text "Default" has to be assigned .. to both tags ..
EDIT: If <node2>is null and <node1> isn t .. then the code shouldn t update <node2> with Default text but it has to be transformed as it is ..

This is the test XML I am 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 the XSLT code I have designed:

   <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[.=  ]"/>

Though my code works, I am not happy with its bulkiness of the code .. Is there anyway to get rid of redundant (if any) lines .... And is there any alternative to use 2 templates to accomplish this (namely template1 and template2), is it possible to reduce the number of templates?

最佳回答

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 importantly, this solution is completely declarative and push-style -- no calling of named templates and the only <xsl:apply-templates> is in the identity 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 quite shorter than the XSLT 1.0 solution.

Something similar is not possible in XSLT 1.0 (such as selecting the first of the union of two nodes without specifying predicates to make the two nodes mutually exclusive), because the node with the default text and the node1/node2 nodes belong to different documents and, as we know well, node ordering between nodes of different documents is implementation specific and is not guaranteed/prescribed.

This solution is completely declarative (no if/then/else) and completely push style: the only <xsl:apply-templates> is in the identity rule.

问题回答
<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>

I have modified Tomalak s answer and accomplished the requirement ..
As I have mentioned in my question, This code passes node2 as null (if it is null) if the sibling node1 isn t null (and also if there is no sibling node1) ..

This code finally became an alternative to the one I have posted in my Q .. (I don t say it is perfect enough .. but I am glad that I could try .. :-)
And this code is more efficient than mine by some 10-20 msecs .. :-)

Here it goes ..

  <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>

Using XSLT 2.0 I would do this, 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>




相关问题
how to represent it in dtd?

I have two element action and guid. guid is a required field when action is add. but when action is del it will not appear in file. How to represent this in dtd ?

.Net application configuration add xml-data

I need to add xml-content to my application configuration file. Is there a way to add it directly to the appSettings section or do I need to implement a configSection? Is it possible to add the xml ...

XStream serializing collections

I have a class structure that I would like to serialize with Xstream. The root class contains a collection of other objects (of varying types). I would like to only serialize part of the objects that ...

MS Word splits words in its XML format

I have a Word 2003 document saved as a XML in WordProcessingML format. It contains few placeholders which will be dynamically replaced by an appropriate content. But, the problem is that Word ...

Merging an XML file with a list of changes

I have two XML files that are generated by another application I have no control over. The first is a settings file, and the second is a list of changes that should be applied to the first. Main ...

How do I check if a node has no siblings?

I have a org.w3c.dom.Node object. I would like to see if it has any other siblings. Here s what I have tried: Node sibling = node.getNextSibling(); if(sibling == null) return true; else ...

Ordering a hash to xml: Rails

I m building an xml document from a hash. The xml attributes need to be in order. How can this be accomplished? hash.to_xml

热门标签