2

Sorry if this has been discussed before, but could nt find a suitable answer for my problem..... I have this xml:

<RootList>
<Root RootAtt1="1" RootAtt2="2" RootAtt3="3">
<Extn ExtnAtt1="1">
<Child ChildAtt1="1" ChildAtt2="2"/>
</Extn>
</Root>
</RootList>

What I want is if any extra attribute comes with this xml at RootList\Root\Extn node apart from what is present, that attribute will be removed.

So if the input comes like:

<RootList>
<Root RootAtt1="1" RootAtt2="2" RootAtt3="3">
<Extn ExtnAtt1="1" ExtnAtt2="2">
<Child ChildAtt1="1" ChildAtt2="2"/>
</Extn>
</Root>
</RootList>

then the output will be like:

<RootList>
<Root RootAtt1="1" RootAtt2="2" RootAtt3="3">
<Extn ExtnAtt1="1">
<Child ChildAtt1="1" ChildAtt2="2"/>
</Extn>
</Root>
</RootList>

For this I have written an xsl as:

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

<RootList>
<xsl:for-each select="/RootList">
<xsl:element name="Root">
<xsl:element name="Extn">
<xsl:attribute name="ExtnAtt1">
<xsl:value-of select="/RootList/Root/Extn/@ExtnAtt1"/>
</xsl:attribute>
</xsl:element>
</xsl:element>
</xsl:for-each>
</RootList>
</xsl:template>
</xsl:stylesheet>

But this gives me the output as:

<?xml version="1.0" encoding="UTF-16"?>
<RootList>
<Root>
<Extn ExtnAtt1="1" />
</Root>
</RootList>

I want to preserve the existing xml and only want remove attributes that are extra.

Can anybody please help me with this.

Thanks in advance guys.

Thanks guys for all the replies...you guys rock.... I figured another method....let me know what do u think about this....

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
   <xsl:output method="xml" indent="yes"/> 
<xsl:template match="/">
<xsl:apply-templates/>
<RootList>
<xsl:for-each select="/RootList">
<xsl:element name="Root">
<xsl:copy-of select="/RootList/Root/@RootAtt1"/>
<xsl:copy-of select="/RootList/Root/@RootAtt2"/>
<xsl:copy-of select="/RootList/Root/@RootAtt3"/>
<xsl:element name="Extn">
<xsl:copy-of select="/RootList/Root/Extn/@ExtnAtt1"/>
<xsl:element name="Child">
<xsl:copy-of select="/RootList/Root/Extn/Child/@ChildAtt1"/>
<xsl:copy-of select="/RootList/Root/Extn/Child/@ChildAtt2"/>
</xsl:element>
</xsl:element>
</xsl:element>
</xsl:for-each>
</RootList>
</xsl:template>
</xsl:stylesheet>

When this is applied on :

<RootList>
<Root RootAtt1="1" RootAtt2="2" RootAtt3="3">
<Extn ExtnAtt1="1" ExtnAtt2="2">
<Child ChildAtt1="1" ChildAtt2="2"/>
</Extn>
</Root>
</RootList>

It gives an output as:

<?xml version="1.0" encoding="UTF-16"?>
<RootList>
<Root RootAtt1="1" RootAtt2="2" RootAtt3="3">
<Extn ExtnAtt1="1">
<Child ChildAtt1="1" ChildAtt2="2" />
</Extn>
</Root>
</RootList>

assuming that RootList\Root\Extn\@ExtnAtt2 is the unwanted attribute.

Thanks Awaiting your reply....:)

Cheers

SMA_JAVA
  • 471
  • 4
  • 9
  • 18
  • If you know which attributes are 'extra' then you also know which attributes are 'not extra' = desired, right? So you can explicitly include all desired attributes using ` – The Nail Jan 02 '12 at 09:27

3 Answers3

2

One way to do this is to override the identity template, and add a template to match the attributes you wish to remove, which will just ignore them.

<xsl:template match="Extn/@*[not(local-name() = 'ExtnAtt1')]" />

So, given the following XSLT:

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

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

   <xsl:template match="Extn/@*[not(local-name() = 'ExtnAtt1')]"/>
</xsl:stylesheet>

When applied to the following XML

<RootList>   
  <Root RootAtt1="1" RootAtt2="2" RootAtt3="3">   
    <Extn ExtnAtt1="1" ExtnAtt2="2">   
      <Child ChildAtt1="1" ChildAtt2="2"/>   
    </Extn>   
  </Root>   
</RootList> 

The following will be output:

<RootList>
  <Root RootAtt1="1" RootAtt2="2" RootAtt3="3">
    <Extn ExtnAtt1="1">
      <Child ChildAtt1="1" ChildAtt2="2"></Child>
    </Extn>
  </Root>
</RootList>

An alternate method, if you require to keep multiple attributes on the Extn element, is to use a stylesheet like so, which explictly lists the attributes to keep:

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

   <xsl:template match="Extn">
      <xsl:copy>
         <xsl:apply-templates select="@ExtnAtt1|@ExtnAtt3|@ExtnAtt5|node()"/>
      </xsl:copy>
   </xsl:template>

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

When applied to the following XML

<RootList>
  <Root RootAtt1="1" RootAtt2="2" RootAtt3="3">
    <Extn ExtnAtt1="1" ExtnAtt2="2" ExtnAtt3="3" ExtnAtt4="4" ExtnAtt5="5">
      <Child ChildAtt1="1" ChildAtt2="2"/>
    </Extn>
  </Root>
</RootList>

The following is output

<RootList>
  <Root RootAtt1="1" RootAtt2="2" RootAtt3="3">
    <Extn ExtnAtt1="1" ExtnAtt3="3" ExtnAtt5="5">
      <Child ChildAtt1="1" ChildAtt2="2"></Child>
    </Extn>
   </Root>
</RootList>

Note that all other elements, apart from the Extn element, are left unaffected.

Tim C
  • 70,053
  • 14
  • 74
  • 93
  • Thanks Tim, But can this be applied in case there are more than one attribute to keep and delete the rest in the Extn tag. For example: Say the xml looks like: and the xml required is : – SMA_JAVA Jan 02 '12 at 11:27
  • @SMA_JAVA: No, Tim's solution assumes that `Extn` must have only one attribute with exact name. See my answer for a completely generic solution that works with any number of attributes, whose names we don't know in advance. – Dimitre Novatchev Jan 02 '12 at 18:14
  • @SMA_JAVA: I've extended my answer to cope with this new requirement. – Tim C Jan 03 '12 at 08:16
0
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
<xsl:output method="xml" indent="yes"/>

<xsl:template match="RootList">
<xsl:apply-templates select="Root"/>
</xsl:template>

<xsl:template match="Root | Extn | Child">
<xsl:copy>
<xsl:apply-templates select="@RootAtt1 | @RootAtt2 | @RootAtt3 | @ExtnAtt1 | @ChildAtt1 | @ChildAtt2" />
<xsl:apply-templates select="Root | Extn | Child"/>
</xsl:copy>
</xsl:template>


<xsl:template match="@RootAtt1 | @RootAtt2 | @RootAtt3 | @ExtnAtt1 | @ChildAtt1 | @ChildAtt2">
<xsl:copy-of select="."/>
</xsl:template>
</xsl:stylesheet>
Treemonkey
  • 2,133
  • 11
  • 24
0

Here is a true solution, that unlike the currently accepted one works with any number of attributes for the elements of the "strict" document:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:my="my:my" exclude-result-prefixes="my">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <my:strict>
    <RootList>
        <Root RootAtt1="1" RootAtt2="2" RootAtt3="3">
            <Extn ExtnAtt1="1" ExtnAtt3="1">
                <Child ChildAtt1="1" ChildAtt2="2"/>
            </Extn>
        </Root>
    </RootList>
 </my:strict>

 <xsl:variable name="vStrict"
      select="document('')/*/my:strict"/>

 <xsl:template match="/">
  <xsl:apply-templates select="*">
      <xsl:with-param name="pstrictElement"
        select="$vStrict/*"/>
  </xsl:apply-templates>
 </xsl:template>

 <xsl:template match="*">
   <xsl:param name="pstrictElement"/>
     <xsl:copy>
       <xsl:apply-templates select="@*">
        <xsl:with-param name="pstrictElement"
             select="$pstrictElement"/>
       </xsl:apply-templates>

       <xsl:apply-templates select="*[1]">
        <xsl:with-param name="pstrictElement"
             select="$pstrictElement/*[1]"/>
       </xsl:apply-templates>

       <xsl:apply-templates select="following-sibling::*[1]">
        <xsl:with-param name="pstrictElement"
             select="$pstrictElement/following-sibling::*[1]"/>
       </xsl:apply-templates>
     </xsl:copy>
 </xsl:template>

 <xsl:template match="@*">
   <xsl:param name="pstrictElement"/>

    <xsl:copy-of select=
    "self::node()
       [$pstrictElement/@*[name() = name(current())]]"/>
 </xsl:template>
</xsl:stylesheet>

When this transformation is applied on the following XML document:

<RootList>
    <Root RootAtt1="1" RootAtt2="2" RootAtt3="3">
        <Extn ExtnAtt1="1" ExtnAtt2="1" ExtnAtt3="1" ExtnAtt4="1">
            <Child ChildAtt1="1" ChildAtt2="2"/>
        </Extn>
    </Root>
</RootList>

the wanted, correct, cleaned result is produced:

<RootList>
   <Root RootAtt1="1" RootAtt2="2" RootAtt3="3">
      <Extn ExtnAtt1="1" ExtnAtt3="1">
         <Child ChildAtt1="1" ChildAtt2="2"/>
      </Extn>
   </Root>
</RootList>
Dimitre Novatchev
  • 240,661
  • 26
  • 293
  • 431