Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

XSLT to select elements that don't match something?

Tags:

xml

xslt

I have the following XML :-

<inventory>
  <item>
    <name_element>
      <!--some structure-->
    </name_element>
    <some_element1>val1</some_element1>
    <some_element2>val2</some_element2>
    <!-- some more elements -->
  </item>
  <!-- lots of items -->
</inventory>

Now I want to convert this to :-

<inventory>
  <item some_element1="val1" some_element2="val2" ... >
    <name_element>
      <!-- as it is -->
    </name_element>
</inventory>

Basically I don't know anything about the names/types of some_elements and any item could have any number of these elements. I do know that they are all simple types and convertible to attributes.

I've read Converting XML elements to XML attributes using XSLT which tells how I can convert all child elements to attributes but it's not clear from that how I could exclude a specific 'name_element' from being converted to an attribute.

like image 389
owagh Avatar asked Dec 20 '22 19:12

owagh


2 Answers

<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0">
    <xsl:output indent="yes" method="xml"/>

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

    <xsl:template match="item">
        <xsl:copy>
            <!--apply templates for any attributes and convert 
                elements(except for name_element) in order to create all 
                attributes before creating child nodes for item element -->
            <xsl:apply-templates select="@* 
                                         | *[not(self::name_element)]"/>
            <!--apply templates to name_element and any comments or processing instructions -->
            <xsl:apply-templates select="name_element 
                                         | comment() 
                                         | processing-instruction()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="item/*[not(self::name_element)]">
        <xsl:attribute name="{local-name()}">
            <xsl:value-of select="."/>
        </xsl:attribute>
    </xsl:template>

</xsl:stylesheet>
like image 54
Mads Hansen Avatar answered Jan 15 '23 07:01

Mads Hansen


First, use the identity template to copy every thing as is:

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

Then, define a template for item. You can iterate over children, and excluding what you don't want be converted to an attribute. this is an example template:

<xsl:template match="item">
 <xsl:copy>
   <!-- this will set children as attributes, but name_element -->
   <xsl:for-each select="*[not(local-name()='name_element')]">
        <xsl:attribute name="{local-name()}">
          <xsl:value-of select="text()"/>
        </xsl:attribute>
   </xsl:for-each>

   <!-- this preserves name_element as it is -->
   <xsl:apply-templates select="name_element"/>
 </xsl:copy>
</xsl:template>
like image 42
Emiliano Poggi Avatar answered Jan 15 '23 07:01

Emiliano Poggi