使用xslt将具有属性的元素转换为新元素

时间:2018-10-25 13:57:56

标签: xml xslt

如何将具有特定属性的元素转换为xslt中带有子元素的新父元素? 更具体地说,每次有一个具有“ WeightUOM”或“ VolumeUOM”属性的元素时,都应创建一个新元素。

愿望是转换具有属性的以下元素:

  <anyWeightElementName WeightUOM="LBS">123.45</anyWeightElementName>
  <anyVolumeElementName VolumeUOM="CUF">0.65</anyVolumeElementName>

为此,一个父元素(无值),一个新的“ Weight”或“ Volume”元素以及一个具有属性值的新的“ WeightUOM”或“ VolumeUOM”元素。

        <anyWeightElementName >
            <Weight>123.45</Weight>
            <WeightUOM>LBS</WeightUOM>
        </anyWeightElementName >
        <anyVolumeElementName>
            <Volume>0.65</Volume>
            <VolumeUOM>CUF</VolumeUOM>
        </anyVolumeElementName>

输入XML

<Response>
<Deliveries>
    <Delivery>
        <EstimatedWeight WeightUOM="LBS">123.45</EstimatedWeight>
        <EstimatedVolume VolumeUOM="CUF">0.65</EstimatedVolume>
        <Customers>
            <Customer>
                <Name>Smith</Name>
                <Zip>12345</Zip>
                <ActualWeight WeightUOM="TON">1.00</ActualWeight>
                <ActualVolume VolumeUOM="CUI">400.00</ActualVolume>
            </Customer>
            <Customer>
                <Name>Jones</Name>
                <Zip>54321</Zip>
                <ActualWeight WeightUOM="TON">0.024</ActualWeight>
                <ActualVolume VolumeUOM="CUF">0.45</ActualVolume>
            </Customer>
        </Customers>
    </Delivery>
</Deliveries>
</Response>

所需的输出

<Response>
<Deliveries>
    <Delivery>
        <EstimatedWeight>
            <Weight>123.45</Weight>
            <WeightUOM>LBS</WeightUOM>
        </EstimatedWeight>
        <EstimatedVolume>
            <Volume>0.65</Volume>
            <VolumeUOM>CUF</VolumeUOM>
        </EstimatedVolume>
        <Customers>
            <Customer>
                <Name>Smith</Name>
                <Zip>12345</Zip>
                <ActualWeight>
                    <Weight>1.00</Weight>
                    <WeightUOM>TON</WeightUOM>
                </ActualWeight>
                <ActualVolume>
                    <Volume>400.00</Volume>
                    <VolumeUOM>CUI</VolumeUOM>
                </ActualVolume>
            </Customer>
            <Customer>
                <Name>Jones</Name>
                <Zip>54321</Zip>
                <ActualWeight>
                    <Weight>0.024</Weight>
                    <WeightUOM>TON</WeightUOM>
                </ActualWeight>
                <ActualVolume>
                    <Volume>0.45</Volume>
                    <VolumeUOM>CUF</VolumeUOM>
                </ActualVolume>
            </Customer>
        </Customers>
    <Delivery>
</Deliveries>
</Response>

尝试过的XSLT

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

<xsl:template match="*">
    <xsl:element name="{local-name()}">
        <xsl:apply-templates/>
    </xsl:element>
</xsl:template>

<xsl:template match="@WeightUOM">
    <xsl:element name="{name(.)}">
        <xsl:element name="Weight">
            <xsl:value-of select="."/>
        </xsl:element>
        <xsl:element name="WeightUOM">
            <xsl:value-of select="@WeightUOM"/>
        </xsl:element>
    </xsl:element>
</xsl:template>

<xsl:template match="@VolumeUOM">
    <xsl:element name="{name(.)}">
        <xsl:element name="Volume">
            <xsl:value-of select="."/>
        </xsl:element>
        <xsl:element name="VolumeUOM">
            <xsl:value-of select="@VolumeUOM"/>
        </xsl:element>
    </xsl:element>
</xsl:template>
</xsl:stylesheet>

这是列出的xslt的当前输出。 xslt不会创建新元素。

<Response>
<Deliveries>
    <Delivery>
        <EstimatedWeight>123.45</EstimatedWeight>
        <EstimatedVolume>0.65</EstimatedVolume>
        <Customers>
            <Customer>
                <Name>Smith</Name>
                <Zip>12345</Zip>
                <ActualWeight>1.00</ActualWeight>
                <ActualVolume>400.00</ActualVolume>
            </Customer>
            <Customer>
                <Name>Jones</Name>
                <Zip>54321</Zip>
                <ActualWeight>0.024</ActualWeight>
                <ActualVolume>0.45</ActualVolume>
            </Customer>
        </Customers>
    </Delivery>
</Deliveries>
</Response>

1 个答案:

答案 0 :(得分:1)

问题在于您使用<xsl:apply-templates/>;这是<xsl:apply-templates select="node()" />的缩写,因此它选择节点,但不选择属性。如果没有选择元素的任何属性,则它们的模板将永远不会匹配。

相反,您需要这样做:

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

实际上,您可能还是想在此处使用“身份模板”

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

此外,在与@WeightUOM匹配的模板中,您当前正在执行<xsl:value-of select="@WeightUOM"/>,但是由于您已经与@WeightUOM匹配,因此这将不返回任何内容,因为它将尝试选择属性当前属性的您需要执行此操作以获取当前属性的值

<xsl:value-of select="."/>

执行此操作以获取父元素的值

<xsl:value-of select="../text()"/>

您也不需要执行<xsl:element name="{name(.)}">,因为匹配*的模板将创建包含元素。 (然后name(.)获得属性的名称。无论如何,您可能应该在这里完成name(..)。)

最后,您可能需要显式匹配子文本节点的模板,以防止它们两次输出

<xsl:template match="*[@WeightUOM]/text()" /> 

尝试使用此XSLT

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

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

<xsl:template match="*[@WeightUOM]/text()" />
<xsl:template match="*[@VolumeUOM]/text()" />

<xsl:template match="@WeightUOM">
    <Weight>
        <xsl:value-of select="../text()"/>
    </Weight>
    <WeightUOM>
        <xsl:value-of select="."/>
    </WeightUOM>
</xsl:template>

<xsl:template match="@VolumeUOM">
    <Volume>
        <xsl:value-of select="../text()"/>
    </Volume>
    <VolumeUOM>
        <xsl:value-of select="."/>
    </VolumeUOM>
</xsl:template>
</xsl:stylesheet>

请注意,如果元素名称为静态,则无需使用xsl:element

或根据需要组合模板...

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

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

<xsl:template match="*[@WeightUOM|@VolumeUOM]/text()" />

<xsl:template match="@WeightUOM|@VolumeUOM">
    <xsl:element name="{substring-before(name(), 'UOM')}">
        <xsl:value-of select="../text()"/>
    </xsl:element>
    <xsl:element name="{name()}">
        <xsl:value-of select="."/>
    </xsl:element>
</xsl:template>
</xsl:stylesheet>