使用xsl 1.0将xml转换为xml。 xsl:attribute:如果已将子元素添加到元素中,则无法向元素添加属性

时间:2014-02-24 10:21:31

标签: xml xslt xmlstarlet

我正在使用xmlstarlet,它只支持xsl 1.0将一个xml转换为另一个xml。

我试图找到属性'atr1'的值,并在同一节点中插入另一个属性'atr2'。

使用以下sample.xml时

<a>
 <b></b>
 <c>
  <d atr1="#{not MGR_1}" atr2="#{condition1}"></d>
  <d atr1="#{ MGR_2}" ></d>
  <d atr1="2"></d>
 </c>
</a>

使用以下transform.xsl

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

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

<xsl:template match="d[contains(@atr1, 'MGR_')]">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>   
<xsl:attribute name="atr2">
<xsl:choose>
  <xsl:when test="@atr2">
   <xsl:value-of select="concat('#{', substring(@atr2,3,string-length(@atr2)-3), ' and not ', substring(@atr1,3))" />            
  </xsl:when>
  <xsl:otherwise>
   <xsl:value-of select="concat('#{not ', substring(@atr1,3))" />
  </xsl:otherwise>
</xsl:choose> 
</xsl:attribute>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

我得到了预期的o / p如下。

<a>
  <b/>
  <c>
    <d atr1="#{not MGR_1}" atr2="#{condition1 and not not MGR_1}"/>
    <d atr1="#{ MGR_2}" atr2="#{not  MGR_2}"/>
    <d atr1="2"/>
  </c>
</a>

但是当我尝试使用下面的xml时。其中有另一个内部元素x,那么问题就出现了。

<a>
 <b></b>
 <c>
  <d atr1="#{not MGR_1}" atr2="#{condition1}"></d>
  <d atr1="#{ MGR_2}" ></d>
  <d atr1="2"></d>
  <d atr1="#{ MGR_3}" >
    <x>blah blah blah</x>
  </d>
 </c>
</a>

试图转换上面的XML。我收到一条错误消息

$ xml tr transform.xsl sample.xml
runtime error: file transform.xsl line 14 element attribute
xsl:attribute: Cannot add attributes to an element if children have been already added to the element.

我在哪里弄错了。什么是正确的XSL来获得修改后的XML所需的o / p。

2 个答案:

答案 0 :(得分:1)

您获得的错误消息实际上是为自己说话。如果已将子节点分配给节点,则无法添加属性。

将模板应用于所有可能的子节点@*,只需将模板应用于所有属性node()即可。请注意,这不仅会捕获子元素

顺便说一下,我只能用Saxon 9.5.1重现你的错误信息,而不能用Saxon 6.5.5和Xalan 2.7.1重现你的错误信息。

<强>样式表

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

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

<xsl:template match="d[contains(@atr1, 'MGR_')]">
<xsl:copy>
<xsl:apply-templates select="@*"/>   
<xsl:attribute name="atr2">
<xsl:choose>
  <xsl:when test="@atr2">
   <xsl:value-of select="concat('#{', substring(@atr2,3,string-length(@atr2)-3), ' and not ', substring(@atr1,3))" />            
  </xsl:when>
  <xsl:otherwise>
   <xsl:value-of select="concat('#{not ', substring(@atr1,3))" />
  </xsl:otherwise>
</xsl:choose> 
</xsl:attribute>
<xsl:apply-templates select="node()"/>  
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

答案 1 :(得分:1)

只有在没有复制其他节点时,才能向节点添加属性。改变这个:

<xsl:template match="d[contains(@atr1, 'MGR_')]">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>   
        <xsl:attribute name="atr2">
            <xsl:choose>
                <xsl:when test="@atr2">
                    <xsl:value-of select="concat('#{', substring(@atr2,3,string-length(@atr2)-3), ' and not ', substring(@atr1,3))" />            
                </xsl:when>
                <xsl:otherwise>
                    <xsl:value-of select="concat('#{not ', substring(@atr1,3))" />
                </xsl:otherwise>
            </xsl:choose> 
        </xsl:attribute>
    </xsl:copy>
</xsl:template>

要:

<xsl:template match="d[contains(@atr1, 'MGR_')]">
    <xsl:copy>
        <xsl:apply-templates select="@*"/>  <!-- changed this -->
        <xsl:attribute name="atr2">
            <xsl:choose>
                <xsl:when test="@atr2">
                    <xsl:value-of select="concat('#{', substring(@atr2,3,string-length(@atr2)-3), ' and not ', substring(@atr1,3))" />            
                </xsl:when>
                <xsl:otherwise>
                    <xsl:value-of select="concat('#{not ', substring(@atr1,3))" />
                </xsl:otherwise>
            </xsl:choose> 
        </xsl:attribute>
        <xsl:apply-templates select="node()"/> <!-- changed this -->
    </xsl:copy>
</xsl:template>