【问题标题】:adding attribute to the node向节点添加属性
【发布时间】:2011-01-28 04:29:28
【问题描述】:

如果子节点值等于某个字符串,我正在尝试向节点添加属性。

我有一个 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,那么它应该将属性 country=32 添加到 Employee 节点。输出应如下所示:

输出.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,然后它应该向所有匹配节点添加属性。

谢谢。

【问题讨论】:

    标签: xslt xslt-2.0


    【解决方案1】:

    第 1 部分

    假设国家 ID 是 等于 32 那么它应该添加 将 country=32 属性分配给 Employee 节点。

    这种转变

    <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. 只有一个模板可以覆盖特定节点的标识规则——Employee 元素的 countryid 子元素的字符串值(转换为数字)32。这个模板将countryid 属性添加到Employee 元素并应用模板来恢复身份规则的活动并按原样复制其他所有内容。

    第 2 部分。

    我们也可以将 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,在某些情况下,我们可能还有其他属性,我想与新属性保持一致。例如 我不知道会有多少属性,所以想保持原样加上新的属性作为 countryid。
    • @atif:提供的转换正是这样做的——只需运行它们即可。 :)
    【解决方案2】:

    除了 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指令很重要(避免在属性之前输出文本节点)

    【讨论】: