简体   繁体   中英

Applying sequential numbering as attribute values of child nodes in one parent node, and then applying these in a different order in a sibling node

Good morning, I have the following (bilingual) XML snippet:

<?xml version="1.0" encoding="UTF-8"?>
<tmx version="1.4">
<body>
    <tu>
        <prop type="x-Context">-2050338055591740051, -2050338055591740051</prop>
        <prop type="x-Origin">TM</prop>
        <prop type="x-ConfirmationLevel">Translated</prop>
        <tuv>
            <seg>
                <ele>The text </ele>
                <ele>
                    <ph x="0" type="QIAsymphony"/>
                </ele>
                <ele> goes </ele>
                <ele>
                    <ph x="0" type="470"/>
                </ele>
                <ele> here </ele>
                <ele>
                    <ph x="0" type="471"/>
                </ele>
                <ele>.</ele>
            </seg>
        </tuv>
        <tuv>
            <seg>
                <ele>El texto </ele>
                <ele>
                    <ph x="0" type="QIAsymphony"/>
                </ele>
                <ele> se mete </ele>
                <ele>
                    <ph x="0" type="471"/>
                </ele>
                <ele> aquí </ele>
                <ele>
                    <ph x="0" type="470"/>
                </ele>
                <ele>.</ele>
            </seg>
        </tuv>
    </tu>
</body>
 </tmx>

I would like to first of all number the x attribute values of the ph elements in the first tuv/seg node, from 1 to 3 (in this case).

However, the result I am getting is this:

<tuv>
            <seg>
                <ele>The text </ele>
                <ele>
                    <ph x="2" type="QIAsymphony"/>
                </ele>
                <ele> goes </ele>
                <ele>
                    <ph x="4" type="470"/>
                </ele>
                <ele> here </ele>
                <ele>
                    <ph x="6" type="471"/>
                </ele>
                <ele>.</ele>
            </seg>
        </tuv>

This is based on the following XSLT Stylesheet:

<xsl:stylesheet version="1.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:template match="tmx">
 <tmx><xsl:attribute name="version"><xsl:value-of select="./@version"/></xsl:attribute>
    <xsl:apply-templates/>
</tmx>
 </xsl:template>


<xsl:template match="body">
<body>
    <xsl:apply-templates/>
</body>
</xsl:template>

<xsl:template match="tuv">
<tuv>
    <xsl:apply-templates/>
</tuv>
</xsl:template>

<xsl:template match="prop">
<prop><xsl:attribute name="type"><xsl:value-of select="./@type"/></xsl:attribute>
    <xsl:apply-templates/>
</prop>
</xsl:template>


<xsl:template match="tu">
<tu>
    <xsl:apply-templates/>
</tu>
</xsl:template>

<xsl:template match="tuv[1]/seg">


<seg>
<xsl:for-each select="ele"> 
<xsl:choose>
<xsl:when test="child::ph">


<ele><ph><xsl:attribute name="x">

<xsl:number/>
</xsl:attribute>

<xsl:attribute name="type">

<xsl:value-of select="ph/@type"/>
</xsl:attribute>


</ph></ele>
</xsl:when>
</xsl:choose>   
 <xsl:choose>
<xsl:when test="child::text()">

<xsl:copy-of select="."/>
</xsl:when>
</xsl:choose>   

</xsl:for-each>



</seg>
</xsl:template>


</xsl:stylesheet>

In order words, I need the following result:

  <tuv>
            <seg>
                <ele>The text </ele>
                <ele>
                    <ph x="1" type="QIAsymphony"/>
                </ele>
                <ele> goes </ele>
                <ele>
                    <ph x="2" type="470"/>
                </ele>
                <ele> here </ele>
                <ele>
                    <ph x="3" type="471"/>
                </ele>
                <ele>.</ele>
            </seg>
        </tuv>

Finally, based on the type attribute values in the first tuv/seg node, I need to apply the corresponding x values to the x attributes in the second tuv/seg node (which in this case will be in a different order):

 <tuv>
            <seg>
                <ele>El texto </ele>
                <ele>
                    <ph x="1" type="QIAsymphony"/>
                </ele>
                <ele> se mete </ele>
                <ele>
                    <ph x="3" type="471"/>
                </ele>
                <ele> aquí </ele>
                <ele>
                    <ph x="2" type="470"/>
                </ele>
                <ele>.</ele>
            </seg>
        </tuv>

Any assistance would be greatly appreciated.

To answer your most immediate question, the reason you are getting the numbers 2, 4, 6 is because of how you use the xsl:number command

<xsl:attribute name="x">
   <xsl:number/>
</xsl:attribute>

You are positioned on an ele element at this point, so will be counting all ele elements, when it looks like you only want to count ones with child ph elements. Therefore you need to do this

<xsl:attribute name="x">
   <xsl:number count="ele[ph]"/>
</xsl:attribute>

However, before answering your next issue about numbering the second tuv element, you need to learn about the XSLT identity transform. Rather than write explicit templates for every single node you wish to copy, you just have a single one to cover all those you wish to change without amending. So you could write your current stylesheet as just this...

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="xml" indent="yes" />

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

    <xsl:template match="tuv[1]/seg/ele[ph]">
        <xsl:copy>
            <xsl:apply-templates select="@*"/>
            <ph type="{ph/@type}">
                <xsl:attribute name="x">
                   <xsl:number count="ele[ph]"/>
                </xsl:attribute>
            </ph>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

But to re-number the second tuv element, consider using a key to look up the ele elements in the first tuv element

<xsl:key name="ph" match="tuv[1]/seg/ele/ph" use="@type" />

Then, to do the numbering, you would have to count the number of preceding siblings for each element

    <xsl:attribute name="x">
        <xsl:value-of select="count(key('ph', ../@type)/../preceding-sibling::ele[ph]) + 1" />
    </xsl:attribute>

This would work in both tuv elements.

Try this XSLT

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
    <xsl:output method="xml" indent="yes" />

    <xsl:key name="ph" match="tuv[1]/seg/ele/ph" use="@type" />

    <xsl:strip-space elements="*" />

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

    <xsl:template match="tuv/seg/ele/ph/@x">
        <xsl:attribute name="x">
            <xsl:value-of select="count(key('ph', ../@type)/../preceding-sibling::ele[ph]) + 1" />
        </xsl:attribute>
    </xsl:template>
</xsl:stylesheet>

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