简体   繁体   中英

How to transform XML using XSLT?

I've got a XML with this structure:

<entities>
    <entity>
        <field>13</field>
    </entity>
    <entity>
        <field>1</field>
    </entity>
    <entity>
        <field>5</field>
    </entity>
</entities>

and I need to transform it to that structure using XSLT:

<entities>
    <entity field="13"/>
    <entity field="1"/>
    <entity field="5"/>
</entities>

That's my XSLT for now:

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

    <xsl:template match="field/text()">
        <xsl:value-of select="concat('&quot;', ., '&quot;')"/>
    </xsl:template>

I'm stuck with transforming <field> into entity's attribute. How to do that?

I would match at the entity level rather than the field level and do something like this:

<xsl:template match="entity">
 <entity>
  <xsl:attribute name="field"><xsl:value-of select="field/text()" /></xsl:attribute>
 </entity>
</xsl:template>

Edit: The above assumes a generic matching template as well, like in the original question. For clarity, here is the full XSL file I tested against the sample input. As noted in the comments, you may want to match only against entity nodes in the entities node, although for the simple sample provided, it doesn't matter.

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="entity">
        <entity>
        <xsl:attribute name="field"><xsl:value-of select="field/text()" /></xsl:attribute>
        </entity>
    </xsl:template>
    <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

This produces the output (tested in Eclipse on Mac):

<?xml version="1.0" encoding="UTF-8"?>
<entities>
    <entity field="13"/>
    <entity field="1"/>
    <entity field="5"/>
</entities>

Try this:

<xsl:for-each select="entities/entity">
    <xsl:element name="entity">
        <xsl:attribute name="field">
            <xsl:value-of select="field"/>
        </xsl:attribute>
    </xsl:element>
</xsl:for-each>

This code will go through all the entity elements and create an element with the field attribute for each one

Somewhat verbose version: Both these might also target the specific internal entity not just "field" but I left it generic since that is all we see in the sample.

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">

  <xsl:template match="entities">
     <entities>
        <xsl:apply-templates/>
     </entities>
  </xsl:template>

  <xsl:template match="entity">
    <entity>
      <xsl:for-each select="*">
        <xsl:attribute name="{name()}">
          <xsl:value-of select="text()"/>
        </xsl:attribute>
      </xsl:for-each>
    </entity>
  </xsl:template>
</xsl:stylesheet>

Output

<entities xmlns="http://www.w3.org/1999/xhtml">
    <entity field="13"></entity>
    <entity field="1"></entity>
    <entity field="5"></entity>
</entities>

Slightly different version with "transform"

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
  <xsl:template match="entities">
    <entities> 
        <xsl:apply-templates/>
    </entities>
  </xsl:template>

  <xsl:template match="entity">
    <entity>
      <xsl:for-each select="*">
        <xsl:attribute name="{name()}">
          <xsl:value-of select="text()"/>
        </xsl:attribute>
      </xsl:for-each>
    </entity>
  </xsl:template>
</xsl:transform>

Target JUST the field inner elements:

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
  <xsl:template match="entities">
    <entities> 
        <xsl:apply-templates/>
    </entities>
  </xsl:template>

  <xsl:template match="entity">
    <entity>
      <xsl:for-each select="field">
        <xsl:attribute name="{name()}">
          <xsl:value-of select="text()"/>
        </xsl:attribute>
      </xsl:for-each>
    </entity>
  </xsl:template>
</xsl:transform>

Example to do copy and just target the field, no closing tag on entity, also omits the xml declaration.

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
  <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
  <xsl:strip-space elements="*"/>
  <xsl:template match="entities">
     <xsl:copy> 
        <xsl:apply-templates/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="entity">
    <xsl:copy>
      <xsl:apply-templates/>
    </xsl:copy>
  </xsl:template>

   <xsl:template match="field">
        <xsl:attribute name="{name()}">
          <xsl:value-of select="text()"/>
        </xsl:attribute>
    </xsl:template>
</xsl:transform>

Output:

<entities>
   <entity field="13"/>
   <entity field="1"/>
   <entity field="5"/>
</entities>

More Compact version of last one:

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
  <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
  <xsl:strip-space elements="*"/>
  <xsl:template match="entities|entity">
     <xsl:copy> 
        <xsl:apply-templates/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="field">
        <xsl:attribute name="{name()}">
          <xsl:value-of select="text()"/>
        </xsl:attribute>
    </xsl:template>
</xsl:transform>

Note This is different from the prior answer, it is more generic, just making ANY nested <field></field> into an attribute on its parent node. This cares nothing about the entities or entity elements specifically and more closely matches your question xsl.

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
  <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
  <xsl:strip-space elements="*"/>
  <xsl:template match="@*|node()[not(self::field)]">
     <xsl:copy> 
        <xsl:apply-templates/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="field">
        <xsl:attribute name="{name()}">
          <xsl:value-of select="text()"/>
        </xsl:attribute>
    </xsl:template>
</xsl:transform>

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