Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create an XSL stylesheet to omit unwanted elements based on input parameters

Tags:

xml

xslt

This is going to be a bit long and specific so please bear with me. I understand how XSLT works but I don't know all of the elements that do the operations. Any help you can provide would be appreciated.

Lets say I have a pilot manual for 737s written in XML. However, there are 3 types of 737s (400, 600, and 800) and although 90% of the manual is the same for all three types there are specific parts that are only for each type. Some pilots will only ever learn about 1 or 2 (or sometimes all 3) jets so I'd like to omit the sections that aren't relevant for them. Here's how I have the XML set up:

<manual>
    <section>A: This is relevant for every type</section>
    <section t600="no" t800="no">B: This is relevant only for the 737-400</section>
    <section t800="no">C: This is relevant for 737-400 and 737-600</section>
    <section t400="no">D: This is relevant for 737-600 and 737-800</section>
</manual>

I want to be able to specify, somehow, that I'm only interestd in, say, the 737-800 and get a manual like this:

<manual>
    <section>A: This is relevant for every type</section>
    <section>D: This is relevant for 737-600 and 737-800</section>
</manual>

Or for a different pilot that is interested in two jets, say the 737-400 and 737-600, the manual would look like this:

<manual>
    <section>A: This is relevant for every type</section>
    <section>B: This is relevant only for the 737-400</section>
    <section>C: This is relevant for 737-400 and 737-600</section>
    <section>D: This is relevant for 737-600 and 737-800</section>
</manual>

I have access to the source XML so if the way I've set it up doesn't make sense I can change it. My thinking was since almost everything is the same for all types it makes more sense to opt-out, but I realize it might make it harder to match? I'm not sure.

Thanks again for taking a look! Let me know if I've left something out.

like image 403
rjcarr Avatar asked Oct 15 '11 17:10

rjcarr


People also ask

What is the purpose of terminate attribute in XSL message?

XSLT <xsl:message> The terminate attribute gives you the choice to either quit or continue the processing when an error occurs.

What is the difference between XSL and XSLT?

XSLT is designed to be used as part of XSL. In addition to XSLT, XSL includes an XML vocabulary for specifying formatting. XSL specifies the styling of an XML document by using XSLT to describe how the document is transformed into another XML document that uses the formatting vocabulary.


1 Answers

I. XSLT 2.0 solution:

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

 <xsl:param name="pInterests">
  <interest topic="t800"/>
 </xsl:param>


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

 <xsl:template match=
  "section[some $t in $pInterests/*/@topic
            satisfies
              not($t = current()/@*[. eq 'no']/name())
          ]
  ">
   <section><xsl:apply-templates/></section>
 </xsl:template>

 <xsl:template match="section"/>
</xsl:stylesheet>

when applied on the provided XML document:

<manual>
    <section>A: This is relevant for every type</section>
    <section t600="no" t800="no">B: This is relevant only for the 737-400</section>
    <section t800="no">C: This is relevant for 737-400 and 737-600</section>
    <section t400="no">D: This is relevant for 737-600 and 737-800</section>
</manual>

produces the wanted, correct result:

<manual>
   <section>A: This is relevant for every type</section>
   <section>D: This is relevant for 737-600 and 737-800</section>
</manual>

If we replace in the transformation the current parameter:

 <xsl:param name="pInterests">
  <interest topic="t800"/>
 </xsl:param>

with:

 <xsl:param name="pInterests">
  <interest topic="t400"/>
  <interest topic="t600"/>
 </xsl:param>

and apply the modified transformation on the same XML document again, we also get the wanted and correct result:

<manual>
   <section>A: This is relevant for every type</section>
   <section>B: This is relevant only for the 737-400</section>
   <section>C: This is relevant for 737-400 and 737-600</section>
   <section>D: This is relevant for 737-600 and 737-800</section>
</manual>

II. XSLT 1.0 solution:

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

 <xsl:param name="pInterests">
  <interest topic="t800"/>
 </xsl:param>

 <xsl:key name="kSectionTypeAttrByName" match="section/@*"
  use="concat(generate-id(..),'|', name())"/>

 <xsl:variable name="vInterests" select=
  "document('')/*/xsl:param[@name='pInterests']/*"/>

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

 <xsl:template match="section">
  <xsl:variable name="vSec" select="."/>

  <xsl:variable name="vHasInterest">
   <xsl:for-each select="$vInterests/@topic">
    <xsl:variable name="vTopic" select="."/>


    <xsl:for-each select=
     "$vSec[not(key('kSectionTypeAttrByName',
                    concat(generate-id(),'|', $vTopic)
                   )
                =
                 'no'
                )
           ]">
      <xsl:text>1</xsl:text>
    </xsl:for-each>

   </xsl:for-each>
  </xsl:variable>

  <xsl:if test="string($vHasInterest)">
   <section><xsl:apply-templates/></section>
  </xsl:if>
 </xsl:template>
</xsl:stylesheet>

when applied on the provided XML document:

<manual>
    <section>A: This is relevant for every type</section>
    <section t600="no" t800="no">B: This is relevant only for the 737-400</section>
    <section t800="no">C: This is relevant for 737-400 and 737-600</section>
    <section t400="no">D: This is relevant for 737-600 and 737-800</section>
</manual>

produces the wanted, correct result:

<manual>
   <section>A: This is relevant for every type</section>
   <section>D: This is relevant for 737-600 and 737-800</section>
</manual>

If we replace in the transformation the current parameter:

 <xsl:param name="pInterests">
  <interest topic="t800"/>
 </xsl:param>

with:

 <xsl:param name="pInterests">
  <interest topic="t400"/>
  <interest topic="t600"/>
 </xsl:param>

and apply the modified transformation on the same XML document again, we also get the wanted and correct result:

<manual>
   <section>A: This is relevant for every type</section>
   <section>B: This is relevant only for the 737-400</section>
   <section>C: This is relevant for 737-400 and 737-600</section>
   <section>D: This is relevant for 737-600 and 737-800</section>
</manual>
like image 109
Dimitre Novatchev Avatar answered Oct 02 '22 00:10

Dimitre Novatchev