How do I remove an attribute and an element if an ancestor's attribute holds a certain value using xsl

1.4k views Asked by At

I am struggling with the following issue: In the below XML, I need to remove the attribute restrict="restrict" if the attribute is nested within an element that also contains an attribute restrict="restrict". I also need to remove the element restrict if it is nested within an element containing an attribute restrict="restrict". However, if a restrict="restrict" appears on a nested element whose ancestor does not contain an attribute restrict="restrict", I need to keep it. The same goes for the restrict element. I need to keep it if it is not nested in a restrict element or within an element that does not have the restrict="restrict" attribute.

<book hasrestrict="yes"> 
<story> <part partno="1"> <parttitle>Title of the Part</parttitle> 
<chapter chapno="11"> <chaptertitle>Title of the Chapter</chaptertitle> 
<section id="id3321234" secno="23">
<sectiontitle>Title of the Section</sectiontitle> 
<toc/> 
<subsection1 restrict="restrict">
<date>(09-23-2012)</date> 
<title>Subsection Title</title> 
<p>Text that makes up the paragraph of the subsetion1. <restrict>This information is  
restricted.</restrict></p> 
<p restrict="restrict">Here is some text in another paragraph that is
restricted. The restrict attribute of the paragraph needs to be
removed because a restrict attribute is already specified for
subsection1. Everything that appears within the element of subsection
1 is already restricted, so I need to remove the unnecessary restrict
attribute from the paragraph because it is causing havoc when my
publishing system tries to format the document.</p>
<subsection2><title>Title of Subsection2</title> 
<p>This is text that appears in a subsecion2 paragraph. 
<list><li>This is an item in a list <restrict>that has restricted content</restrict></li>
<li>Second item</li> 
<li>Third item</li></list></p> 
<subsection3 restrict="restrict">
<title>Title of Subsection3</title> 
<p>Text appearing in a paragraph in subsction 3 
<table frame="all"> <tgroup cols="3" colsep="1" rowsep="1"> 
<colspec colname="col1"/> <colspec colname="col2"/> 
<colspec colname="col3"/>
<thead><row><entry>Entry1</entry>
<entry>Entry2</entry>
<entry>Entry3 <restrict>with restricted information</restrict></entry></row></thead>
<tbody><row><entry restrict="restrict">text</entry>
<entry>text</entry>
<entry>text</entry></row>
</tbody></tgroup></table></p></subsection3></subsection2></subsection1>
<subsection1><date>(09-23-2012)</date> 
<title>Subsection Title</title>
<p>Text that makes up the paragraph of the subsetion1. <restrict>This information is
restricted.</restrict></p> 
<p restrict="restrict">Here is some text in another paragraph that is
restricted. </p> 
<subsection2 restrict="restrict"><title>Title of Subsection2</title>
<p>This is text that appears in a subsecion2 paragraph.</p>
</subsection2></subsection1></section></chapter></part></story></book>

I am new to xsl, and have attempted to write the translation, but I am not having much luck.

The desired out put should look like this:

<book hasrestrict="yes">
<story>
<part partno="1">
<parttitle>Title of the Part</parttitle>
<chapter chapno="11">
<chaptertitle>Title of the Chapter</chaptertitle>
<section id="id3321234" secno="23">
<sectiontitle>Title of the Section</sectiontitle>
<toc/>
<subsection1 restrict="restrict"><date>(09-23-2012)</date>
<title>Subsection Title</title>
<p>Text that makes up the paragraph of the subsetion1. This information is restricted.</p>
<p>Here is some text in another paragraph that is restricted. The restrict attribute of the paragraph needs to be removed because a restrict attribute is already specified for subsection1. Everything that appears within the element of subsection1 is already restricted, so I need to remove the unnecessary restrict attribute from the paragraph because it is causing havoc when my publishing system tries to format the document.</p>
<subsection2><title>Title of Subsection2</title>
<p>This is text that appears in a subsecion2 paragraph.
<list>
<li>This is an item in a list that has restricted content</li>
<li>Second item</li>
<li>Third item</li></list></p>
<subsection3><title>Title of Subsection3</title>
<p>Text appearing in a paragraph in subsction 3
<table frame="all">
<tgroup cols="3" colsep="1" rowsep="1">
<colspec colname="col1"/>
<colspec colname="col2"/>
<colspec colname="col3"/>
<thead><row><entry>Entry1</entry>
<entry>Entry2</entry>
<entry>Entry3 with restricted information</entry></row></thead>
<tbody><row>text</entry>
<entry>text</entry>
<entry>text</entry></row></tbody></tgroup></table></p></subsection3></subsection2></subsection1>
<subsection1><date>(09-23-2012)</date>
<title>Subsection Title</title><p>Text that makes up the paragraph of the subsetion1. <restrict>This information is restricted.</restrict></p>
<p restrict="restrict">Here is some text in another paragraph that is restricted. </p>
<subsection2 restrict="restrict"><title>Title of Subsection2</title><p>This is text that appears in a subsecion2 paragraph.</p></subsection2></subsection1></section></chapter></part></story></book>

Here is the code I have attempted:

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

<xsl:template match="@restrict">
  <xsl:if test="ancestor::*[@restrict='restrict']">
    <xsl:copy>
      <xsl:apply-templates/>
    </xsl:copy>
  </xsl:if>
</xsl:template>

<xsl:template match="restrict">
  <xsl:if test="ancestor::*[@restrict='restrict']">
    <xsl:copy>
      <xsl:apply-templates/>
    </xsl:copy>
  </xsl:if>
</xsl:template>

<xsl:template match="restrict"> 
  <xsl:if test="ancestor::restrict">
    <xsl:copy>
      <xsl:apply-templates/>
    </xsl:copy>
  </xsl:if>
</xsl:template>     
</xsl:transform>

This is not working for me. I receive an error stating

"WARNING org.xml.sax.SAXParseException: The child axis starting at an attribute node will never select anything"

I really don't know how to go about doing this. Any suggestions (and explanation) would be greatly appreciated.

2

There are 2 answers

8
Tim C On BEST ANSWER

As you are using the identity template, you don't really need to code extra logic for the elements or attributes you wish to keep; whether they are restrict ones or not. You only really need to add templates for the things you wish to remove.

You say "need to remove the attribute restrict="restrict" if the attribute is nested within an element that also contains an attribute restrict="restrict". Well, the template to do this would be like so:

<xsl:template match="../ancestor::*[@restrict='restrict']]" />

(The .. here is to ignore the current element which has the current restrict element)

And to "to remove the element restrict if it is nested within an element containing an attribute restrict="restrict".", the template would look like this

<xsl:template match="restrict[ancestor::*[@restrict='restrict']]" /> 

Having said that, you mention about keeping restrict elements if they are not nested in a restrict element or within an element that does not have the restrict="restrict" attribute. In this case, the templates may need to look like this:

<xsl:template match="@restrict[../ancestor::restrict or ../ancestor::*[@restrict='restrict']]" />
<xsl:template match="restrict[ancestor::restrict or ancestor::*[@restrict='restrict']]" /> 

Try this XSLT

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

    <xsl:template match="@restrict[../ancestor::restrict or ../ancestor::*[@restrict='restrict']]" />        
    <xsl:template match="restrict[ancestor::restrict or ancestor::*[@restrict='restrict']]" /> 
</xsl:transform>
3
michael.hor257k On

If I understand this correctly, you want to do:

XSLT 1.0

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

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

<!-- remove the attribute restrict="restrict" if the attribute is nested within an element that also contains an attribute restrict="restrict". -->
<xsl:template match="@restrict[.='restrict'][parent::*/ancestor::*/@restrict='restrict']"/>


<!-- remove the element restrict if it is nested within an element containing an attribute restrict="restrict".  -->
<xsl:template match="restrict[ancestor::*/@restrict='restrict']">
    <xsl:apply-templates select="@*|node()"/>
</xsl:template>

</xsl:stylesheet>