modify part of XML element name and replace with i

2019-07-19 16:25发布

问题:

Is it possible to use XSLT to change part of the element name to another element name with increment number.

Like I only want to change the element name with UPC_ at the beginning

<product>
    <UPC_US>123</UPC_US>
    <UPC_UK>223</UPC_UK>
    <UPC_JP>345</UPC_JP>
    <other>unchange</other>
</product>
<product>
    <UPC_US>1234</UPC_US>
    <UPC_CA>1235</UPC_CA>
    <other>unchange</other>
</product>

to this?

<product>
    <UPC_1>123</UPC_1>
    <UPC_2>223</UPC_2>
    <UPC_3>345</UPC_3>
    <other>unchange</other>
</product>
<product>
    <UPC_1>1234</UPC_1>
    <UPC_2>1235</UPC_2>
    <other>unchange</other>
</product>

回答1:

This can be achieved quite easily with XSLT. First of all you would create a template to match elements beginning with UPC

<xsl:template match="product/*[starts-with(local-name(), 'UPC')]">

Then you would create a new element, with your revised name, based on the element's position

<xsl:element name="UPC_{position()}">

Note the use of "Attribute Value Templates" here in creating the name. The curly braces indicate this is an expression to be evaluated, not output literally.

Here is the full XSLT

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

   <xsl:template match="product/*[starts-with(local-name(), 'UPC')]">
      <xsl:element name="UPC_{position()}">
         <xsl:apply-templates select="@*|node()"/>
      </xsl:element>
   </xsl:template>

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

When applied your XML, the following is output

<products>
   <product>
      <UPC_1>123</UPC_1>
      <UPC_2>223</UPC_2>
      <UPC_3>345</UPC_3>
      <other>unchange</other>
   </product>
   <product>
      <UPC_1>1234</UPC_1>
      <UPC_2>1235</UPC_2>
      <other>unchange</other>
   </product>
</products>

Having said that, it would only work if your UPC elements are always the first elements. If you had this as input

<product>
    <UPC_US>123</UPC_US>
    <UPC_UK>223</UPC_UK>
    <UPC_JP>345</UPC_JP>
    <other>unchange</other>
</product>

The output would be this

<products>
   <product>
      <UPC_1>123</UPC_1>
      <UPC_2>223</UPC_2>
      <other>unchange</other>
      <UPC_4>345</UPC_4>
   </product>
</products>

If this is not what you want, you could instead make use of xsl:number to count the elements. Try this XSLT instead

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

   <xsl:template match="product/*[starts-with(local-name(), 'UPC')]">
      <xsl:variable name="count">
         <xsl:number count="*[starts-with(local-name(), 'UPC')]"/>
      </xsl:variable>
      <xsl:element name="UPC_{$count}">
         <xsl:apply-templates select="@*|node()"/>
      </xsl:element>
   </xsl:template>

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

In this case, the second example would yield this

<products>
    <product>
        <UPC_1>123</UPC_1>
        <UPC_2>223</UPC_2>
        <other>unchange</other>
        <UPC_3>345</UPC_3>
    </product>
</products>

In both cases, make note of the use of the Indentity Transform template to copy all other elements as-is.



回答2:

Yes it is possible. xsl:element allows you to create a new element name dynamically.



标签: xml xslt