Шаблон вызова xslt с динамическим соответствием - PullRequest
6 голосов
/ 10 декабря 2010

Я пытаюсь установить динамический параметр при вызове шаблона для подавления узлов из XML.

Я бы назвал этот шаблон следующим образом:

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>

Можем ли мы достичь результата, используя этот или другой способ?Идеальный способ - передать разделенные запятыми аббревиатуры узлов и подавить их за один вызов.

Любая помощь приветствуется.

С уважением,

AB

Ответы [ 3 ]

7 голосов
/ 11 декабря 2010

Вам не нужен XSLT 2.0 для этой обработки .

Это преобразование XSLT 1.0:

<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>

при применении к предоставленному документу XML (с добавленной инструкцией обработки, чтобы продемонстрировать, как мы можем также удалять PI - не только элементы):

<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>

дает требуемые, правильные результаты :

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

Обратите внимание :

  1. Это чистое преобразование XSLT 1.0 .

    2. Удаляются не только элементы, но и инструкции обработки , когда их имя указывается с помощью внешнего параметра $pNodesToSuppress.

4 голосов
/ 10 декабря 2010

Я не понимаю, почему значение параметра "fname" подавляет элемент с именем "firstname", но помимо этого вы можете просто захотеть

<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>

[править] Я пропустил, что ваша примерная таблица стилей, казалось, содержала сопоставление этих параметров с именами элементов в образце ввода. В этом случае вот адаптированная версия таблицы стилей для этого случая:

<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>

[второе редактирование для реализации дальнейшего запроса] Вот пример, который также проверяет значения атрибута relationship:

<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>

Применительно к

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

вывод

<Employees>
  <Employee>

    <firstname>xyz</firstname>


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

Вы можете добавить пробел и вывести indent = "yes", чтобы исключить пустые строки.

3 голосов
/ 17 января 2013

Если вы используете старый Saxon-B или более новый Saxon-PE или Saxon-EE в качестве процессора XSLT, вы можете использовать расширение saxon для выполнения динамических шаблонных вызовов:

<saxon:call-template name="{$templateName}"/>

Не забудьте объявить пространство имен саксонии в элементе xsl-stylesheet:

<xsl:stylesheet xmlns:saxon="http://saxon.sf.net/" [...] >
...