modify part of XML element name and replace with i

2019-07-19 15:48发布

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>

标签: xml xslt
2条回答
ゆ 、 Hurt°
2楼-- · 2019-07-19 16:36

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.

查看更多
迷人小祖宗
3楼-- · 2019-07-19 16:43

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

查看更多
登录 后发表回答