Split XML nodes based on particular text

2020-05-01 08:51发布

Please suggest for how to split the xml nodes based on particular comment text (<!--Break-->). I tried thru using xsl:text disable-output-escaping format to place the required closing and opening tags (elements).

  • My code is static, how to make that dynamic code which will work for any numbers of ancestor with respect to comment text, i.e., if ancestor count 'n', then from n to 1 how to make use of call-template method.
  • some empty nodes are presented which are not ancestor to the content, how to avoid these

I referred some suggestions from our site, but find difficult to understand the logics. Please suggest. (XSLT 2 version)

XSLT:

<root>
    <a>
        <b>The text1
            <c>
                <d>The text2</d><d>The text3</d><!--Break--><d>The text4</d>
                <e>The text5<!--Break--></e>
            </c>
            <f>The text6</f>
        </b>
    </a>
</root>

XSLT:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
   <xsl:template match="@*|node()">
     <xsl:copy><xsl:apply-templates select="node()|@*"/></xsl:copy>
   </xsl:template>

   <xsl:template match="//comment()[contains(., 'Break')]">
       <xsl:variable name="varAncestorCount" select="count(ancestor::*)"/>
       <xsl:choose>
           <xsl:when test="$varAncestorCount = 4">
               <xsl:text disable-output-escaping="yes">&lt;/</xsl:text><xsl:value-of select="ancestor::*[1]/name()"/><xsl:text disable-output-escaping="yes">&gt;</xsl:text>
                   <xsl:text disable-output-escaping="yes">&lt;/</xsl:text><xsl:value-of select="ancestor::*[2]/name()"/><xsl:text disable-output-escaping="yes">&gt;</xsl:text>
                       <xsl:text disable-output-escaping="yes">&lt;/</xsl:text><xsl:value-of select="ancestor::*[3]/name()"/><xsl:text disable-output-escaping="yes">&gt;</xsl:text>
                           <xsl:text disable-output-escaping="yes">&lt;/</xsl:text><xsl:value-of select="ancestor::*[4]/name()"/><xsl:text disable-output-escaping="yes">&gt;</xsl:text>
                               <xsl:comment><xsl:value-of select="."/></xsl:comment>
                           <xsl:text disable-output-escaping="yes">&lt;</xsl:text><xsl:value-of select="ancestor::*[4]/name()"/><xsl:text disable-output-escaping="yes">&gt;</xsl:text>    
                       <xsl:text disable-output-escaping="yes">&lt;</xsl:text><xsl:value-of select="ancestor::*[3]/name()"/><xsl:text disable-output-escaping="yes">&gt;</xsl:text>
                   <xsl:text disable-output-escaping="yes">&lt;</xsl:text><xsl:value-of select="ancestor::*[2]/name()"/><xsl:text disable-output-escaping="yes">&gt;</xsl:text>
               <xsl:text disable-output-escaping="yes">&lt;</xsl:text><xsl:value-of select="ancestor::*[1]/name()"/><xsl:text disable-output-escaping="yes">&gt;</xsl:text>
           </xsl:when>
           <xsl:when test="$varAncestorCount = 5">
               <xsl:text disable-output-escaping="yes">&lt;/</xsl:text><xsl:value-of select="ancestor::*[1]/name()"/><xsl:text disable-output-escaping="yes">&gt;</xsl:text>
                   <xsl:text disable-output-escaping="yes">&lt;/</xsl:text><xsl:value-of select="ancestor::*[2]/name()"/><xsl:text disable-output-escaping="yes">&gt;</xsl:text>
                       <xsl:text disable-output-escaping="yes">&lt;/</xsl:text><xsl:value-of select="ancestor::*[3]/name()"/><xsl:text disable-output-escaping="yes">&gt;</xsl:text>
                           <xsl:text disable-output-escaping="yes">&lt;/</xsl:text><xsl:value-of select="ancestor::*[4]/name()"/><xsl:text disable-output-escaping="yes">&gt;</xsl:text>
                               <xsl:text disable-output-escaping="yes">&lt;/</xsl:text><xsl:value-of select="ancestor::*[5]/name()"/><xsl:text disable-output-escaping="yes">&gt;</xsl:text>
                                   <xsl:comment><xsl:value-of select="."/></xsl:comment>
                               <xsl:text disable-output-escaping="yes">&lt;</xsl:text><xsl:value-of select="ancestor::*[5]/name()"/><xsl:text disable-output-escaping="yes">&gt;</xsl:text>    
                           <xsl:text disable-output-escaping="yes">&lt;</xsl:text><xsl:value-of select="ancestor::*[4]/name()"/><xsl:text disable-output-escaping="yes">&gt;</xsl:text>    
                       <xsl:text disable-output-escaping="yes">&lt;</xsl:text><xsl:value-of select="ancestor::*[3]/name()"/><xsl:text disable-output-escaping="yes">&gt;</xsl:text>
                   <xsl:text disable-output-escaping="yes">&lt;</xsl:text><xsl:value-of select="ancestor::*[2]/name()"/><xsl:text disable-output-escaping="yes">&gt;</xsl:text>
               <xsl:text disable-output-escaping="yes">&lt;</xsl:text><xsl:value-of select="ancestor::*[1]/name()"/><xsl:text disable-output-escaping="yes">&gt;</xsl:text>
           </xsl:when>
       </xsl:choose>

   </xsl:template>

 </xsl:stylesheet>

Desired Output:

<root>
    <a>
        <b>The text1
            <c>
                <d>The text2</d>
                <d>The text3</d>
            </c>
        </b>
    </a>
</root>
<!--Break-->
<root>
    <a>
        <b>
            <c>
                <d>The text4</d>
                <e>The text5</e>
            </c>
        </b>
    </a>
</root>
<!--Break-->
<root>
    <a>
        <b>
            <f>The text6</f>
        </b>
    </a>
</root>

标签: xslt
1条回答
淡お忘
2楼-- · 2020-05-01 09:14

[groan] Where do you find these "interesting" tasks?

You should NEVER be required to form XML nodes manually, using escaped text.

Try the following stylesheet. It could probably be more streamlined, but I believe it addresses the main issue here:

XSLT 2.0

<xsl:stylesheet version="2.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:variable name="root" select="/*" />

<xsl:template match="/">
    <super-root>
        <xsl:for-each select="0 to count(//comment()[.='Break'])">
            <xsl:apply-templates select="$root">
                <xsl:with-param name="i" select="." tunnel="yes"/>
            </xsl:apply-templates>
            <xsl:if test="position()!=last()">
                <xsl:comment>Break</xsl:comment>
            </xsl:if>
        </xsl:for-each >
    </super-root>
</xsl:template>

<xsl:template match="@*|node()">
    <xsl:param name="i" tunnel="yes"/>
        <xsl:if test="descendant-or-self::text()[count(preceding::comment()[.='Break'])=$i]">
            <xsl:copy>
                <xsl:apply-templates select="@*|node()"/>
            </xsl:copy>
        </xsl:if>
</xsl:template>

</xsl:stylesheet>

Result:

<?xml version="1.0" encoding="UTF-8"?>
<super-root>
   <root>
      <a>
         <b>The text1
            <c>
               <d>The text2</d>
               <d>The text3</d>
            </c>
         </b>
      </a>
   </root>
   <!--Break-->
   <root>
      <a>
         <b>
            <c>
               <d>The text4</d>
               <e>The text5</e>
            </c>
         </b>
      </a>
   </root>
   <!--Break-->
   <root>
      <a>
         <b>
            <f>The text6</f>
         </b>
      </a>
   </root>
</super-root>
查看更多
登录 后发表回答