Srikrishna Pothukuchi
Srikrishna Pothukuchi

Reputation: 41

Transform XML elements into Attributes

I have to transform one XML file into another set suitable for specific needs. Transforming XML is not new, but transforming it dynamically from elements to Attributes is somewhat intriguing.

Also there is a need to query the parent element using child element's text value.

This is what I want to achieve (data is anonymized):

Source File

<PARTS>
    <PART>
        <Name>Valve</Name>
        <Code>1</Code>
        <Color>Brown</Color>
    </PART>
    <PART>
        <Name>Filter</Name>
        <Code>2</Code>
        <Color>Green</Color>
    </PART>
    <PART>
        <Name>Plug</Name>
        <Code>3</Code>
        <Color>Brown</Color>
    </PART>
</PARTS>

Transform to target XML file 1, filtering on color sub-element:

<PARTS>
    <PART Name="Valve" Code=1 Color="Brown" />
    <PART Name="Plug" Code=3 Color="Brown" />
</PARTS>

Transform to target XML file 2, filtering on color sub-element:

<PARTS>
    <PART Name="Filter" Code=2 Color="Green" />
</PARTS>

Upvotes: 1

Views: 1079

Answers (1)

Flynn1179
Flynn1179

Reputation: 12075

You have a number of options here. The brute force method is just to use attribute value templates (putting an attributes value as an expression in {..}) to do

<xsl:template match="PART">
  <PART Name="{Name}" Code="{Code}" Color="{Color}"/>
</xsl:template>

or a more generalized

<xsl:template match="PART">
  <xsl:for-each select="*">
    <xsl:attribute name="local-name()">
      <xsl:value-of select="."/>
    </xsl:attribute>
  </xsl:for-each>
</xsl:template>

Or you could generalize handle the child elements with a generalized template on child nodes of PART:

<xsl:template match="PART/*">
  <xsl:attribute name="local-name()">
    <xsl:value-of select="."/>
  </xsl:attribute>
</xsl:template>

You can also match "Name | Color | Code" or "*[parent::PART]", but the latter is less performant.

As far as filtering out unwanted nodes goes, that's a separate process. If you define an appropriate matching template with no content, it'll not produce anything in the output. So in your first case, you can do

<xsl:template match="PART[Code=2]"/>

And this part will be processed by that template and output nothing. A template with this pattern will have a higher priority than a simple <xsl:template match="PART"> template, but I'd recommend adding a priority attribute to both to be clear.

You can also invert the logic easily enough with PART[Code!=2] for your second case. Obviously, extrapolate this to your specific needs.

Upvotes: 2

Related Questions