Convert XML attributes to elements using XSLT

流过昼夜 提交于 2021-02-10 08:44:47

问题


I am trying to transform input XML documents attributes to elements using the following stylesheet:

<?xml version="1.0"?>
      <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
      <xsl:output method="xml"/>
      <xsl:template match="*">
        <xsl:element name="{name()}">
          <xsl:for-each select="@*[name()!='type']">
            <xsl:element name="{name()}">
              <xsl:value-of select="."/>
            </xsl:element>
          </xsl:for-each>
          <xsl:apply-templates select="*|text()"/>
        </xsl:element>
      </xsl:template>
      </xsl:stylesheet>

However, despite correctly creating new elements based on each element attributes, "leaf" elements (i.e., with no children) have their text mixed with created elements. E.g.

Input XML

<order>
  <id>12345</id>
  <date>Today</date>
  <location country="PT">LX</location>
  <location country="ES">Barcelona</location>
  <items type="array" stock="true">
    <item>
      <id type="array">item1</id>
      <spec>
        <color type="array">brown</color>
        <price currency="euro">20</price>
        <usage>office</usage>
        <usage>home</usage>
      </spec>
    </item>
  </items>
</order>

Output XML

<order>
  <id>12345</id>
  <date>Today</date>
  <location>
    <country>PT</country>LX
  </location>
  <location>
    <country>ES</country>Barcelona
  </location>
  <items>
    <stock>true</stock>
    <item>
      <id>item1</id>
      <spec>
        <color>brown</color>
        <price>
          <currency>euro</currency>20
        </price>
        <usage>office</usage>
        <usage>home</usage>
      </spec>
    </item>
  </items>
</order>

What I want

    ...
<location>
    <text>LX</text>
    <country>PT</country>
  </location>
  <location>
    <text>Barcelona</text>
    <country>ES</country>
  </location>
...
        <price>
          <text>20</text>
          <currency>euro</currency>
        </price>
       ...

What I tried

<?xml version="1.0"?>
      <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
      <xsl:output method="xml"/>
      <xsl:template match="*">
        <xsl:element name="{name()}">
          <xsl:for-each select="@*[name()!='type']">
            <xsl:element name="text">
              <xsl:value-of select=".."/>
            </xsl:element>
            <xsl:element name="{name()}">
               <xsl:value-of select="."/>
             </xsl:element>
          </xsl:for-each>
          <xsl:apply-templates select="*|text()"/>
        </xsl:element>
      </xsl:template>
      </xsl:stylesheet>

Problem Despite correctly creating new element tag

  <text>20</text>

it also creates "text" element on "non-leaf" elements (i.e., elements with children), getting for the provided example:

...
<items>
    <text>item1brown20officehome</text>
    <stock>true</stock>
    <item>
      <id>item1</id>
      <spec>
        <color>brown</color>
        <price>
          <text>20</text>
          <currency>euro</currency>20
        </price>
        <usage>office</usage>
        <usage>home</usage>
      </spec>
    </item>
  </items>
...

I do not want

<text>item1brown20officehome</text>

Any ideas? Thanks in advance.


回答1:


I'm not sure if this creates the exact output you want because you only included a portion of your desired output, but try something like this...

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

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

  <xsl:template match="*[@*]/text()">
    <text><xsl:value-of select="."/></text>
  </xsl:template>

  <xsl:template match="@*">
    <xsl:element name="{name()}">
      <xsl:value-of select="."/>
    </xsl:element>
  </xsl:template>

</xsl:stylesheet>

Fiddle: http://xsltfiddle.liberty-development.net/jyH9rLX

What if I want to keep only attributes whose name is "type" as attributes (not converting them to elements, but keeping them as they were originally). How could I achieve such goal?

Updated XSLT

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

  <xsl:template match="node()">
    <xsl:copy>
      <xsl:apply-templates select="@type|text()"/>
      <xsl:apply-templates 
        select="@*[not(name()='type')]|*|comment()|processing-instruction()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="*[@*]/text()">
    <text><xsl:value-of select="."/></text>
  </xsl:template>

  <xsl:template match="@type">
    <xsl:copy-of select="."/>
  </xsl:template>

  <xsl:template match="@*">
    <xsl:element name="{name()}">
      <xsl:value-of select="."/>
    </xsl:element>
  </xsl:template>

</xsl:stylesheet>

Updated fiddle: http://xsltfiddle.liberty-development.net/jyH9rLX/2



来源:https://stackoverflow.com/questions/49798164/convert-xml-attributes-to-elements-using-xslt

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!