简体   繁体   中英

XSLT Close nodes in xml if child node encountered

I have the following XML input :

<?xml version="1.0" encoding="UTF-8"?>
<parent>
    <para>
        <emphasis>
            <emphasis>
                blah 0 
                <Xref ref="1"/>
                blah 1
            </emphasis>
        </emphasis>
    </para>
    <para>
        blah 2 <Xref ref="2"/> blah 3
    </para>
</parent>

I would like to get the Xref nodes outside the para nodes, no matter how deep they are.

Using the following XSLT 1.0 code:

<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="xml" doctype-public="XSLT-compat" omit-xml-declaration="yes" encoding="UTF-8" indent="yes" />

    <xsl:template match="@*|node()" priority="0">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:key name="kPrecedingXref" match="node()[not(self::Xref)]" use="generate-id(following-sibling::Xref[1])"/>

    <xsl:template match="para[Xref]" priority="1">
        <xsl:apply-templates select="Xref"/>
        <xsl:if test="count(Xref/following-sibling::node())&gt;0">
            <para>
                <xsl:apply-templates select="Xref/following-sibling::node()[not(self::Xref) and not(following-sibling::Xref)]"/>
            </para>
        </xsl:if>
    </xsl:template>

    <xsl:template match="Xref" priority="1">
        <xsl:if test="parent::para">
            <para>
                <xsl:apply-templates select="key('kPrecedingXref', generate-id())"/>
            </para>
        </xsl:if>

        <toolRef><xsl:value-of select="@ref"/></toolRef>
    </xsl:template>

    <xsl:template match="emphasis[Xref]" priority="1">
        <xsl:copy>
                <xsl:apply-templates select="node()[following-sibling::Xref]" />
        </xsl:copy>
        <xsl:apply-templates select="Xref"/>
        <xsl:copy>
                <xsl:apply-templates select="node()[preceding-sibling::Xref]" />
        </xsl:copy>
    </xsl:template>

</xsl:transform>

that handles this but can only get the Xref nodes outside of their direct parent node. How can I change this code to get this:

Wanted output

<parent>
   <para>
      <emphasis>
         <emphasis>
            blah 0 
         </emphasis>
      </emphasis>
   </para>
   <toolRef>1</toolRef>
   <para>
      <emphasis>
         <emphasis>
            blah 1
         </emphasis>
      </emphasis>
   </para>
   <para>blah 2 </para>
   <toolRef>2</toolRef>
   <para> blah 3</para>
</parent>

Here's the full example code: http://xsltransform.net/nbiCsZM/4

Thanks to michael.hor257k and a lot of fiddling around, I finally found a solution to my problem.

Here's my working XSLT 1.0 code

<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="xml" encoding="UTF-8" indent="yes" />

    <xsl:template match="@*|node()" priority="0">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="@*|node()" priority="0" mode="EXTRACT_XREF">
        <xsl:param name="i" select="0"/>
        <xsl:if test="descendant-or-self::text()[count(preceding-sibling::Xref)=$i]">
            <xsl:copy>
                <xsl:apply-templates select="@*"/>
                <xsl:apply-templates select="node()" mode="EXTRACT_XREF">
                    <xsl:with-param name="i" select="$i"/>
                </xsl:apply-templates>
            </xsl:copy>
        </xsl:if>
    </xsl:template>

    <xsl:template match="para[descendant::Xref]" priority="1">
        <xsl:call-template name="recursive">
            <xsl:with-param name="node" select="."/>
            <xsl:with-param name="count" select="0"/>
            <xsl:with-param name="limit" select="count(descendant::Xref)"/>
        </xsl:call-template>
    </xsl:template>

    <xsl:template name="recursive">
        <xsl:param name="node"/>
        <xsl:param name="count" select="0"/>
        <xsl:param name="limit"/>
        <xsl:if test="$count&lt;=$limit">
            <xsl:apply-templates select="$node" mode="EXTRACT_XREF">
                <xsl:with-param name="i" select="$count"/>
            </xsl:apply-templates>
            <xsl:apply-templates select="$node/descendant::Xref[$count + 1]"/>
            <xsl:call-template name="recursive">
                <xsl:with-param name="node" select="$node"/>
                <xsl:with-param name="count" select="$count + 1"/>
                <xsl:with-param name="limit" select="$limit"/>
            </xsl:call-template>
        </xsl:if>
    </xsl:template>

    <xsl:template match="Xref" priority="1">
        <toolRef>
            <xsl:attribute name="ref">
                <xsl:value-of select="@ref"/>
            </xsl:attribute>
        </toolRef>
    </xsl:template>

</xsl:transform>

I alaso added some testing nodes that should be copied without changes, to test out the all possible cases:

Input XML

<?xml version="1.0" encoding="UTF-8"?>
<parent>
    <dontChangeThisNode></dontChangeThisNode>
    <para>
        <emphasis mode="bold">
            <emphasis mode="underline">blah 0<Xref ref="1"/>blah 1<Xref ref="3"/> blah</emphasis>
        </emphasis>
    </para>
    <dontChangeThisNodeEither>hop</dontChangeThisNodeEither>
    <para>blah 2 <Xref ref="2"/> blah 3</para>
    <para>blah 4</para>
</parent>

And finally, here's what this code transforms it to:

XML output

<?xml version="1.0" encoding="UTF-8"?>
<parent>
   <dontChangeThisNode/>
   <para>
      <emphasis mode="bold">
         <emphasis mode="underline">blah 0</emphasis>
      </emphasis>
   </para>
   <toolRef ref="1"/>
   <para>
      <emphasis mode="bold">
         <emphasis mode="underline">blah 1</emphasis>
      </emphasis>
   </para>
   <toolRef ref="3"/>
   <para>
      <emphasis mode="bold">
         <emphasis mode="underline"> blah</emphasis>
      </emphasis>
   </para>
   <dontChangeThisNodeEither>hop</dontChangeThisNodeEither>
   <para>blah 2 </para>
   <toolRef ref="2"/>
   <para> blah 3</para>
   <para>blah 4</para>
</parent>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM