Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

xslt call template with dynamic match

i am trying to paas the dynamic parameter while calling the template to suppress nodes from the xml.

I would call this template like:

transform employee.xml suppress.xsl ElementsToSuppress=id,fname 

employee.xml

<?xml version="1.0" encoding="utf-8" ?>
<Employees>
  <Employee>
    <id>1</id>
    <firstname>xyz</firstname>
    <lastname>abc</lastname>
    <age>32</age>
    <department>xyz</department>
  </Employee>
  <Employee>
    <id>2</id>
    <firstname>XY</firstname>
    <lastname>Z</lastname>
    <age>21</age>
    <department>xyz</department>
  </Employee>
</Employees>

Suppress.xsl

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="2.0" xmlns:elements="http://localhost">

  <elements:name abbrev="id">id</elements:name>
  <elements:name abbrev="fname">firstname</elements:name>

  <xsl:param name="ElementsToSuppress" ></xsl:param>

  <xsl:variable name="tokenizedSample" select="tokenize($ElementsToSuppress,',')"/>
  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
    <xsl:for-each select="$tokenizedSample">
      <xsl:call-template name ="Suppress"  >
        <xsl:with-param  name="parElementName">
          <xsl:value-of select="."/>
        </xsl:with-param>
      </xsl:call-template>
    </xsl:for-each>

  </xsl:template>






  <xsl:template name="Suppress">
    <xsl:param name="parElementName" select="''"></xsl:param>
    <xsl:variable name="extNode" select="document('')/*/elements:name[@abbrev=$parElementName]"/>
    <xsl:call-template name="test" >
      <xsl:with-param name="parElementName" >
        <xsl:value-of select="$extNode"/>
      </xsl:with-param>
    </xsl:call-template>
  </xsl:template>


  <xsl:template name="test"  match="*[name() = $parElementName]" >
    <xsl:param name="parElementName" select="''"></xsl:param>
    <xsl:call-template name="SuppressElement" />    
  </xsl:template>

  <xsl:template name="SuppressElement" />

</xsl:stylesheet>

Can we achieve output by using this or some other way? The ideal way is to pass the comma separated abbreviations of nodes and suppress them in one call.

Any help will be appreciated.

Regards,

AB

like image 862
atif Avatar asked Dec 10 '10 16:12

atif


2 Answers

You don't need XSLT 2.0 for this processing.

This XSLT 1.0 transformation:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:my="my:my">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>
 <xsl:param name="pNodesToSuppress"
      select="'id,fname,pi'"/>

  <my:toSuppress>
   <name abbrev="id">id</name>
   <name abbrev="fname">firstname</name>
   <name abbrev="pi">somePI</name>
  </my:toSuppress>

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

 <xsl:variable name="vNamesToSupress">
  <xsl:for-each select=
   "$vToSuppressTable
     [contains(concat(',',$pNodesToSuppress,','),
               concat(',',@abbrev, ',')
               )
     ]">
   <xsl:value-of select="concat(',',.)"/>
   </xsl:for-each>
   <xsl:text>,</xsl:text>
 </xsl:variable>

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

 <xsl:template match="node()" priority="0.01">
  <xsl:if test=
     "not(contains($vNamesToSupress,
                   concat(',',name(),',')
                   )
          )">
   <xsl:call-template name="identity"/>
  </xsl:if>
 </xsl:template>
</xsl:stylesheet>

when applied on the provided XML document (with an added processing instruction to demonstrate how we can also delete PIs -- not only elements):

<Employees>
  <Employee>
    <id>1</id>
    <firstname>xyz</firstname>
    <lastname>abc</lastname>
    <age>32</age>
    <department>xyz</department>
  </Employee>
  <?somePI This PI will be deleted ?>
  <Employee>
    <id>2</id>
    <firstname>XY</firstname>
    <lastname>Z</lastname>
    <age>21</age>
    <department>xyz</department>
  </Employee>
</Employees>

produces the wanted, correct results:

<Employees>
   <Employee>
      <lastname>abc</lastname>
      <age>32</age>
      <department>xyz</department>
   </Employee>
   <Employee>
      <lastname>Z</lastname>
      <age>21</age>
      <department>xyz</department>
   </Employee>
</Employees>

Do note:

  1. This is a pure XSLT 1.0 transformation.

    2.Not only elements, but also processing instructions are deleted, when their name is specified via the external parameter $pNodesToSuppress.

like image 117
Dimitre Novatchev Avatar answered Sep 19 '22 01:09

Dimitre Novatchev


I don't get why the parameter value "fname" would suppress an element called "firstname" but apart from that you might simply want

<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  exclude-result-prefixes="xs"
  version="2.0">

  <xsl:param name="ElementsToSuppress" as="xs:string" select="'id,firstname'"/>
  <xsl:variable name="names-to-suppress" as="xs:QName*"
    select="for $s in tokenize($ElementsToSuppress, ',') return QName('', $s)"/>

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

  <xsl:template match="*[node-name(.) = $names-to-suppress]"/>

</xsl:stylesheet>

[edit] I missed that your sample stylesheet seemed to contain a mapping from those parameters to the elements names in the sample input. In that case here is an adapted version of the stylesheet to handle that case:

<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:data="http://example.com/data"
  xmlns:elements="http://example.com/elements"
  exclude-result-prefixes="xs data elements"
  version="2.0">

  <data:data>
    <elements:name abbrev="id">id</elements:name>
    <elements:name abbrev="fname">firstname</elements:name>
  </data:data>

  <xsl:key name="e-by-abbrev" match="elements:name" use="@abbrev"/>

  <xsl:param name="ElementsToSuppress" as="xs:string" select="'id,fname'"/>
  <xsl:variable name="names-to-suppress" as="xs:QName*"
    select="for $s in tokenize($ElementsToSuppress, ',') return QName('', key('e-by-abbrev', $s, document('')/xsl:stylesheet/data:data))"/>

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

  <xsl:template match="*[node-name(.) = $names-to-suppress]"/>

</xsl:stylesheet>

[second edit to implement further request] Here is a sample that also checks the relationship attribute values:

<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:data="http://example.com/data"
  xmlns:elements="http://example.com/elements"
  exclude-result-prefixes="xs data elements"
  version="2.0">

  <xsl:param name="ElementsToSuppress" as="xs:string" select="'id'"/>

  <xsl:param name="parVAObjectRelationship" as="xs:string" select="'a,b'"/>

  <xsl:variable name="names-to-suppress" as="xs:QName*"
    select="for $s in tokenize($ElementsToSuppress, ',') return QName('', key('e-by-abbrev', $s, document('')/xsl:stylesheet/data:data))"/>

  <xsl:variable name="att-values-to-suppress" as="xs:string*"
    select="tokenize($parVAObjectRelationship, ',')"/>

  <data:data>
    <elements:name abbrev="id">id</elements:name>
    <elements:name abbrev="fname">firstname</elements:name>
  </data:data>

  <xsl:key name="e-by-abbrev" match="elements:name" use="@abbrev"/>

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

  <xsl:template match="*[@relationship = $att-values-to-suppress]"/>

  <xsl:template match="*[node-name(.) = $names-to-suppress]"/>

</xsl:stylesheet>

When applied to

<Employees>
  <Employee>
    <id>1</id>
    <firstname>xyz</firstname>
    <lastname relationship="a">abc</lastname>
    <age relationship="b">32</age>
    <department>xyz</department>
  </Employee>
</Employees>

the output is

<Employees>
  <Employee>

    <firstname>xyz</firstname>


    <department>xyz</department>
  </Employee>
</Employees>

You might want to add strip-space and output indent="yes" to prevent the blank lines.

like image 45
Martin Honnen Avatar answered Sep 18 '22 01:09

Martin Honnen