向节点添加属性

16

我试图在子节点的值等于某个字符串时,向该节点添加属性。

我有一个名为 main.xml 的文件。

<Employees>    
 <Employee>     
 <countryid>32</countryid>
 <id name="id">1</id>
 <firstname >ABC</firstname>
 <lastname >XYZ</lastname>     
 </Employee>
 <Employee>     
 <countryid>100</countryid>
 <id name="id">2</id>
 <firstname >ddd</firstname>
 <lastname >ggg</lastname>     
 </Employee>
 </Employees>    

假设国家ID等于32,则应该在Employee节点中添加属性country=32。输出结果应如下所示:

output.xml

 <Employees>    
 <Employee countryid="32">     
 <countryid>32</countryid>
 <id name="id">1</id>
 <firstname >ABC</firstname>
 <lastname >XYZ</lastname>     
 </Employee>
 <Employee>     
 <countryid>100</countryid>
 <id name="id">2</id>
 <firstname >ddd</firstname>
 <lastname >ggg</lastname>     
 </Employee>
 </Employees>    

我正在使用以下脚本,但是出现了错误:无法在包含元素的子元素之后创建属性节点:

Transform.xsl


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

<xsl:template match="Employees/Employee/countryid[.=32']">
  <xsl:attribute name="countryid">32</xsl:attribute>
  <xsl:copy>
    <xsl:apply-templates select="@* | node()"/>
  </xsl:copy>
</xsl:template>

非常感谢任何帮助。我还想知道我们是否可以将countryid作为逗号分隔的值传递,这样我就可以传递32、100,然后它应该将属性添加到所有匹配的节点。

谢谢。

2个回答

17

第一部分.

假设国家ID等于32,那么应该向Employee节点添加属性country=32。

进行以下转换:

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

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

 <xsl:template match="Employee[countryid=32]">
  <Employee countryid="{countryid}">
   <xsl:apply-templates select="@*|node()"/>
  </Employee>
 </xsl:template>
</xsl:stylesheet>

应用于提供的 XML 文档时

<Employees>
    <Employee>
        <countryid>32</countryid>
        <id name="id">1</id>
        <firstname >ABC</firstname>
        <lastname >XYZ</lastname>
    </Employee>
    <Employee>
        <countryid>100</countryid>
        <id name="id">2</id>
        <firstname >ddd</firstname>
        <lastname >ggg</lastname>
    </Employee>
</Employees>

产生所需、正确的结果:

<Employees>
   <Employee countryid="32">
      <countryid>32</countryid>
      <id name="id">1</id>
      <firstname>ABC</firstname>
      <lastname>XYZ</lastname>
   </Employee>
   <Employee>
      <countryid>100</countryid>
      <id name="id">2</id>
      <firstname>ddd</firstname>
      <lastname>ggg</lastname>
   </Employee>
</Employees>

说明:

  1. 使用身份规则(identity rule)将每个节点原样复制。使用和覆盖身份规则(模板)是最基本和强大的XSLT设计模式。

  2. 仅有一个模板可以覆盖特定节点的身份规则——具有将字符串值(转换为数字)32的countryid子元素的Employee元素。此模板向Employee元素添加countryid属性,并应用模板以恢复身份规则的活动,并将其他所有内容全部原样复制。

第二部分。

我们也可以将countryid作为逗号分隔的值传递,以便我可以传递32,100,然后它应将属性添加到所有匹配的节点中

这个转换:

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

 <xsl:param name="pIds" select="'32,100'"/>

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

 <xsl:template match="Employee">
  <Employee>
   <xsl:if test=
     "contains(concat(',',$pIds,','),
               concat(',',countryid,',')
               )">
    <xsl:attribute name="countryid">
      <xsl:value-of select="countryid"/>
    </xsl:attribute>
   </xsl:if>
   <xsl:apply-templates select="@*|node()"/>
  </Employee>
 </xsl:template>
</xsl:stylesheet>

应用于相同的XML文档(如上),会产生所需的正确结果

<Employees>
   <Employee countryid="32">
      <countryid>32</countryid>
      <id name="id">1</id>
      <firstname>ABC</firstname>
      <lastname>XYZ</lastname>
   </Employee>
   <Employee countryid="100">
      <countryid>100</countryid>
      <id name="id">2</id>
      <firstname>ddd</firstname>
      <lastname>ggg</lastname>
   </Employee>
</Employees>

Dimitre,像往常一样感谢你,但我想复制节点员工并添加新属性@countryid,在某些情况下,我们可能还有其他属性,我希望保留它们以及新属性。例如:<Employee countryid=32 class="xyz"> 我不知道会有多少属性,所以希望将它们保持原样,并添加新的countryid属性。 - atif
@atif:提供的转换正是这样做的——只需运行它们即可看到。 :) - Dimitre Novatchev

8
除了Dimitre的好答案之外,这是一个XSLT 2.0样式表:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:param name="pCountry" select="'32,100'"/>
    <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="Employee[countryid = tokenize($pCountry,',')]">
        <Employee countryid="{countryid}">
            <xsl:apply-templates select="@*|node()"/>
        </Employee>
    </xsl:template>
</xsl:stylesheet>

输出:

<Employees>
    <Employee countryid="32">
        <countryid>32</countryid>
        <id name="id">1</id>
        <firstname>ABC</firstname>
        <lastname>XYZ</lastname>
    </Employee>
    <Employee countryid="100">
        <countryid>100</countryid>
        <id name="id">2</id>
        <firstname>ddd</firstname>
        <lastname>ggg</lastname>
    </Employee>
</Employees>

注意: 序列的存在性比较,模式中的参数/变量引用。

另一种做法是假设countryid总是第一个子元素:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:strip-space elements="*"/>
    <xsl:param name="pCountry" select="'32,100'"/>
    <xsl:template match="node()|@*" name="identity">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="countryid[. = tokenize($pCountry,',')]">
        <xsl:attribute name="countryid">
            <xsl:value-of select="."/>
        </xsl:attribute>
        <xsl:call-template name="identity"/>
    </xsl:template>
</xsl:stylesheet>
注意: 现在xsl:strip-space指令非常重要(避免在属性之前输出文本节点)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接