0

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?

2
  • 1
    One way is to make your template match field and use the xsl:attribute instruction to create an attribute. Alternatively match entity and use an attribute value template. Commented Sep 20, 2017 at 15:04
  • Not 100% sure your use desire so I posted one answer with specificity to the node names entities,entity, and one not. Commented Sep 20, 2017 at 16:25

4 Answers 4

2

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>
Sign up to request clarification or add additional context in comments.

3 Comments

<entities></entities> ist missing.
@fhossfel: It still works with the sample XML. Without knowing more, I'm not sure I should assume they want/need to only look within entities. I'm assuming there is still the generic template match as well, so I will edit to clarify.
I liked your solution. Had already upvoted but now it is already better.
0

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

Comments

0

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>

Comments

0

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>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.