嵌套元素到属性

时间:2014-03-03 18:52:14

标签: xml templates xslt

我的输入xml是

  <?xml version="1.0" encoding="UTF-8"?>
  <foobar>
    <foo>
        <a>
            <a>
                <a>atr1</a>
                <v>NO</v>
            </a>
            <a>
                <a>more</a>
                <v>more</v>
            </a>
        </a>
        <v>ONE</v>
    </foo>
    <bar>
        <baz>
            <a>
                <a>
                    <a>attr</a>
                    <v>123</v>
                </a>
                <a>
                    <a>attr222</a>
                    <v>22</v>
                </a>
            </a>
            <v>TWO</v>
        </baz>
        <a>
            <a>
                <a>atr6</a>
                <v>ATR</v>
                </a>
           </a>
 </bar>
  <a>
   <a>
      <a>atr0</a>
      <v>NO</v>
  </a>
  <a>
      <a>atr2</a>
      <v>NO</v>
</a>
</a>
</foobar>

我想要的输出是

<?xml version="1.0" encoding="UTF-8"?>
<foobar atr0="NO" atr2="NO">
    <foo atr1="NO" more="more">ONE</foo>
    <bar atr6="ATR">
        <baz attr="123" attr222="22">TWO</baz>
    </bar>
</foobar>   

我正在尝试将所有嵌套元素转换为属性。 我的xslt脚本是 -

   <?xml version="1.0" encoding="utf-8"?>
   <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="*"/>

<!-- Matches all of nodes -->
<xsl:template match = "node()">
<xsl:copy>
<xsl:for-each select= "*">
  <xsl:attribute name="{//a[text()]}">
    <xsl:value-of select="//v[text()]"/>
  </xsl:attribute>  
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

脚本正在将第一个foobar元素转换为仅属性。我做错了什么?

2 个答案:

答案 0 :(得分:1)

你没有xsl:apply-templates所以你永远不会超过第一个根元素。此外,node()包含文本,注释和处理说明,如果您匹配其中一个属性,则不希望尝试创建属性。

尝试这样的事情......

XSLT 1.0

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

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

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

    <xsl:template match="a/a" priority="1">
        <xsl:attribute name="{a}">
            <xsl:value-of select="v"/>
        </xsl:attribute>
    </xsl:template>

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

</xsl:stylesheet>

<强>输出

<foobar atr0="NO" atr2="NO">
   <foo atr1="NO" more="more">ONE</foo>
   <bar atr6="ATR">
      <baz attr="123" attr222="22">TWO</baz>
   </bar>
</foobar>

答案 1 :(得分:0)

这是您的代码正在做的事情:

您正在选择一个 node()foobar,当您在其中时,您有一个for-each并在那里选择任何当前上下文中的节点:*,因此您匹配foo,然后从那里获得包含文本的所有//a(唯一的一个:atr1)和所有//v包含文字NO。由于您从不调用<xsl:apply-templates>或使用某个表达式来处理树中的节点,因此不会调用其他模板。

您需要递归处理其他节点。此外,您不能只使用//a,因为在某些节点中有多个a,您最终会将所有节点连接起来。

一种方法是使用多个递归模板。这是一个可能的解决方案:

<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:output indent="yes"/>

    <xsl:template match="*[not(self::a)][not(self::v)]">
        <xsl:copy>
            <xsl:if test="a">
                <xsl:apply-templates select="a" />   
            </xsl:if>
            <xsl:apply-templates select="*[not(self::a)]"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="a[following-sibling::v][text()]">
        <xsl:attribute name="{normalize-space(.)}">
            <xsl:value-of select="following-sibling::v" />
        </xsl:attribute>  
    </xsl:template>

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

</xsl:stylesheet>