шаблон вызова xslt с динамическим соответствием

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

Я бы назвал этот шаблон так:

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

сотрудник.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>

Подавить.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


person atif    schedule 10.12.2010    source источник
comment
atif, подумайте над тем, чтобы опубликовать образец вывода, который вы хотите создать для опубликованного вами входного образца, тогда мы могли бы предложить способ XSLT 2.0 для достижения этого.   -  person Martin Honnen    schedule 10.12.2010
comment
transform employee.xml подавлять.xsl ElementsToSuppress=id,fname Вот вывод: ‹?xml version=1.0 encoding=utf-8 ?› ‹Employees› ‹Employee› ‹lastname›abc‹/lastname› ‹age›32‹/ age› ‹department›xyz‹/department› ‹/Employee› ‹Employee› ‹lastname›Z‹/lastname› ‹age›21‹/age› ‹department›xyz‹/department› ‹/Employee› ‹/Employees› Пожалуйста учтите, что он должен быть параметризован, потому что я мог бы передать параметры для удаления других узлов. Как и в случае преобразования employee.xml submit.xsl ElementsToSuppress=lname,age, теперь в выходных данных должны быть удалены только фамилия и возраст. Спасибо   -  person atif    schedule 10.12.2010
comment
Хороший вопрос, +1. Смотрите мой ответ для простого и не длинного решения XSLT 1.0. Приятно знать, что эту проблему можно легко решить с помощью XSLT 1.0, и в конечном итоге XSLT 2.0 не требуется. :)   -  person Dimitre Novatchev    schedule 11.12.2010
comment
Также обратите внимание, что мое решение обрабатывает не только элементы, но и инструкции по обработке.   -  person Dimitre Novatchev    schedule 11.12.2010


Ответы (3)


Я не понимаю, почему значение параметра "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", чтобы предотвратить появление пустых строк.

person Martin Honnen    schedule 10.12.2010
comment
Я хочу еще больше изменить его, скажем, если я добавлю еще один параметр parVAObjectRelationship, и я хочу проверить, соответствуют ли значения атрибута переданному параметру parVAObjectRelationship, а затем подавить эти узлы. transform employee.xml подавлять.xsl ElementsToSuppress=id parVAObjectRelationship=a,b ‹?xml version=1.0 encoding=utf-8 ?› ‹Employees› ‹Employee› ‹id›1‹/id› ‹firstname›xyz‹/firstname› ‹отношение фамилии=a›abc‹/фамилия› ‹отношение возраста=b›32‹/возраст› ‹отдел›xyz‹/отдел› ‹/сотрудник› ‹/сотрудники› Спасибо за помощь - person atif; 10.12.2010
comment
@Martin Honnen: Для встроенных данных в XSLT 2.0 я бы использовал функцию @as из xsl:param. Это делает document('') не нужным для получения набора узлов, что позволяет избежать проблем с нулевым URI (динамические документы). - person ; 10.12.2010
comment
Любые идеи, ребята, как мы можем заставить его удалить узел с определенным значением атрибута? Эти значения атрибутов должны быть переданы в качестве параметра, как и в сообщении выше. ‹отношение фамилий=какое-то значение›xxxx‹/фамилия› Спасибо - person atif; 10.12.2010
comment
atif, я добавил код таблицы стилей, чтобы показать, как использовать значение атрибута. - person Martin Honnen; 11.12.2010
comment
Привет, Мартин! Когда я пытаюсь изменить ваш скрипт, выполнив следующие изменения, я получаю сообщение об ошибке Произошла ошибка сопоставления шаблона {[@relationship = $relationship-to-suppress]}: невозможно преобразовать строку в тип {xs:QName } ‹xsl:param name=parVAObjectRelationship as=xs:string select='age,lname'/› ‹xsl:variable name=relationship-to-suppress as=xs:QName select=for $s в tokenize( $parVAObjectRelationship, ',') return QName('', key('e-by-rabbrev', $s, document('')/xsl:stylesheet/rdata:rdata))/› ‹xsl:template match=* [@relationship = $relationship-to-suppress]/› Пожалуйста, помогите. – - person atif; 13.12.2010
comment
atif, образец, который я разместил, имеет <xsl:variable name="att-values-to-suppress" as="xs:string*" select="tokenize($parVAObjectRelationship, ',')"/>, что имеет смысл, поскольку вы хотите сравнить значение атрибута атрибута relationship с переданной строкой, а не с QName. Я не уверен, почему вы изменили это, вам нужно сначала уточнить, чего вы хотите достичь. - person Martin Honnen; 14.12.2010
comment
Я хочу сделать то же самое, что и раньше, когда я передал аббревиатуры отношений, а затем сопоставил их на основе элемента данных для фактического значения. Причина в том, что наши имена отношений еще не завершены, и они продолжают меняться, поэтому я подумал, что Я сопоставлю его в сценарии xls, а затем просто передам аббревиатуру из наших make-файлов. У нас есть несколько make-файлов, поэтому идея состоит в том, что я должен изменить только файл xsl, не внося никаких изменений в мои make-файлы. - person atif; 14.12.2010
comment
Я не уверен, что понимаю ваше последнее требование, вы можете просто захотеть <xsl:variable name="relationship-to-suppress" as="xs:string*" select="for $s in tokenize($parVAObjectRelationship, ',') return key('e-by-rabbrev', $s, document('')/xsl:stylesheet/rdata:rdata)"/>, а затем <xsl:template match="*[@relationship = $relationship-to-suppress]/>. QName будет иметь смысл только в том случае, если вы сравните его с результатом функции node-name. Если этот комментарий не поможет, отредактируйте свой исходный пост, чтобы предоставить читаемые многострочные образцы кода, я боюсь, что обмен кодом здесь, в комментарии, отстой. - person Martin Honnen; 14.12.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.

person Dimitre Novatchev    schedule 11.12.2010
comment
Привет, Мартин! Когда я пытаюсь изменить ваш скрипт, выполнив следующие изменения, я получаю сообщение об ошибке Произошла ошибка сопоставления шаблона {[@relationship = $relationship-to-suppress]}: невозможно преобразовать строку в тип {xs:QName } ‹xsl:param name=parVAObjectRelationship as=xs:string select='age,lname'/› ‹xsl:variable name=relationship-to-suppress as=xs:QName select=for $s в tokenize( $parVAObjectRelationship, ',') return QName('', key('e-by-rabbrev', $s, document('')/xsl:stylesheet/rdata:rdata))/› ‹xsl:template match=* [@relationship = $relationship-to-suppress]/› Пожалуйста, помогите. - person atif; 13.12.2010
comment
@atif: вы не комментируете ответ Мартина :) - person Dimitre Novatchev; 13.12.2010
comment
Спасибо, что сообщили мне :) Ваше решение работает слишком хорошо, но мне действительно нужно работать над 2.0, потому что я хочу применить трансформацию к нескольким файлам. - person atif; 13.12.2010
comment
@aif: это решение отлично работает не только с XSLT 1.0, но и с XSLT 2.0. Ничто не мешает вам выбрать его. Просто измените атрибут version на 2.0 - person Dimitre Novatchev; 13.12.2010

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

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

Не забудьте объявить saxon-Namespace в элементе xsl-stylesheet:

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