开发者

adding attribute to the node

I am trying to add an attribute to the node if the child node value is equal to some stri开发者_JS百科ng.

I have a main.xml file

<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>    

So let's say if the country id is equal to 32 then it should add attribute country=32 to Employee node. The output should be like below :

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>    

I am using the following script but getting error that An attribute node cannot be create after the children of containing element.:

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>

Any help will be appreciated. Also can we pass countryid as comma seprated values so that i can pass 32,100 and then it should add attribute to all the matching nodes.

Thanks.


Part 1.

So let's say if the country id is equal to 32 then it should add attribute country=32 to Employee node.

This transformation:

<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>

when applied on the provided XML document:

<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>

produces the wanted, correct result:

<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>

Explanation:

  1. The identity rule is used to copy every node as-is. Using and overriding the identity rule (template) is the most fundamental and powerful XSLT design pattern.

  2. There is only one template that overrides the identity rule for specific nodes -- Employee elements that have a countryid child with string value (converted to number) 32. This template adds a countryid attribute to the Employee element and applies templates to resume the activity of the identity rule and copy everything else as-is.

Part 2.

Also can we pass countryid as comma seprated values so that i can pass 32,100 and then it should add attribute to all the matching nodes

This transformation:

<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>

when applied to the same XML document (above), produces the wanted, correct result:

<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>


In addition to Dimitre's good answer, an XSLT 2.0 stylesheet:

<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>

Output:

<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>

Note: Existencial comparison with sequence, param/variable reference in patterns.

Other approach assuming countryid is always first child:

<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>

Note: Now xsl:strip-space instruction is important (avoids output text node before attribute)

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜