简体   繁体   中英

Transforming an XML file to another XML file using XSLT

I have a simple xml file I would like to transform using XSLT.

The first node is the schema(header), the following nodes are the actual data. Notice that there are some missing fields in the data.

<ex:file xmlns:ex="http://example.com">
  <ex:header>
    <ex:title>houseNumber</ex:title>
    <ex:title>street</ex:title>
    <ex:title>city</ex:title>
    <ex:title>state</ex:title>
  </ex:header>
  <ex:address>
    <ex:field>108</ex:field>
    <ex:field>Ridgewood Circle</ex:field>
    <ex:field>Rochester</ex:field>
    <ex:field>NY</ex:field>
  </ex:address>
  <ex:address>
    <ex:field/>
    <ex:field>W. Clark</ex:field>
    <ex:field>Springfield</ex:field>
    <ex:field>IL</ex:field>
  </ex:address>
</ex:file>

We would like to transform this XML in two ways. First, we want to use the information in the "header" and substitute the four ex:field elements, in such a way I can get an XML that looks like:

<ex:file xmlns:ex="http://example.com">
  <ex:header>
    <ex:title>houseNumber</ex:title>
    <ex:title>street</ex:title>
    <ex:title>city</ex:title>
    <ex:title>state</ex:title>
  </ex:header>
  <ex:address>
    <ex:houseNumber>108</ex:houseNumber>
    <ex:street>Ridgewood Circle</ex:street>
    <ex:city>Rochester</ex:city>
    <ex:state>NY</ex:state>
  </ex:address>
  <ex:address>
    <ex:houseNumber/>
    <ex:street>W. Clark</ex:street>
    <ex:city>Springfield</ex:city>
    <ex:state>IL</ex:state>
  </ex:address>
</ex:file>

The second transformation is to eliminate those empty element:

<ex:file xmlns:ex="http://example.com">
  <ex:header>
    <ex:title>houseNumber</ex:title>
    <ex:title>street</ex:title>
    <ex:title>city</ex:title>
    <ex:title>state</ex:title>
  </ex:header>
  <ex:address>
    <ex:houseNumber>108</ex:houseNumber>
    <ex:street>Ridgewood Circle</ex:street>
    <ex:city>Rochester</ex:city>
    <ex:state>NY</ex:state>
  </ex:address>
  <ex:address>
    <ex:street>W. Clark</ex:street>
    <ex:city>Springfield</ex:city>
    <ex:state>IL</ex:state>
  </ex:address>
</ex:file>

Notice that the "houseNumber" in the second "record" is gone.

I have gone through the XSLT tutorial ( http://www.w3schools.com/xsl/default.asp ) several times and I am not sure if this can be actually done.

Thanks in advance to all.

The following transformation solves your first problem. It is an identity transform with one additional template that specifies what should be done to each ex:field element.

XSLT Stylesheet

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:ex="http://example.com">

    <xsl:output method="xml" indent="yes"/>
    <xsl:strip-space elements="*"/>

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

    <xsl:template match="ex:address">
        <xsl:copy>
            <xsl:for-each select="ex:field">
            <xsl:variable name="pos" select="position()"/>
                <xsl:element name="{concat('ex:',../../ex:header/ex:title[position() = $pos])}">
                    <xsl:apply-templates select="@*|node()"/>
                </xsl:element>
            </xsl:for-each>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

XML Output

<?xml version="1.0" encoding="utf-8"?>
<ex:file xmlns:ex="http://example.com">
   <ex:header>
      <ex:title>houseNumber</ex:title>
      <ex:title>street</ex:title>
      <ex:title>city</ex:title>
      <ex:title>state</ex:title>
   </ex:header>
   <ex:address>
      <ex:houseNumber>108</ex:houseNumber>
      <ex:street>Ridgewood Circle</ex:street>
      <ex:city>Rochester</ex:city>
      <ex:state>NY</ex:state>
   </ex:address>
   <ex:address>
      <ex:houseNumber/>
      <ex:street>W. Clark</ex:street>
      <ex:city>Springfield</ex:city>
      <ex:state>IL</ex:state>
   </ex:address>
</ex:file>

To also exclude empty input elements, it is better to reformulate the above for-each as a separate template matching ex:field elements. Then, add a second template that matches ex:field elements if they do not contain any text.

XSLT Stylesheet

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:ex="http://example.com">

    <xsl:output method="xml" indent="yes"/>
    <xsl:strip-space elements="*"/>

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

    <xsl:template match="ex:field">
        <xsl:variable name="pos" select="position()"/>
                <xsl:element name="{concat('ex:',../../ex:header/ex:title[position() = $pos])}">
                    <xsl:apply-templates select="@*|node()"/>
                </xsl:element>
    </xsl:template>

    <xsl:template match="ex:field[not(text())]"/>

</xsl:stylesheet>

Final XML Output

<?xml version="1.0" encoding="utf-8"?>
<ex:file xmlns:ex="http://example.com">
   <ex:header>
      <ex:title>houseNumber</ex:title>
      <ex:title>street</ex:title>
      <ex:title>city</ex:title>
      <ex:title>state</ex:title>
   </ex:header>
   <ex:address>
      <ex:houseNumber>108</ex:houseNumber>
      <ex:street>Ridgewood Circle</ex:street>
      <ex:city>Rochester</ex:city>
      <ex:state>NY</ex:state>
   </ex:address>
   <ex:address>
      <ex:street>W. Clark</ex:street>
      <ex:city>Springfield</ex:city>
      <ex:state>IL</ex:state>
   </ex:address>
</ex:file> 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM