Предоставляет ли XSLT средство для идентификации элементов XML с помощью регулярных выражений? - PullRequest
3 голосов
/ 20 мая 2011

У меня есть пример XML-файла, который выглядит следующим образом:

--- before transformation ---
<root-node>

   <child-type-A> ... </child-type-A>
   <child-type-A> ... </child-type-A>
   <child-type-B> ... </child-type-B>
   <child-type-C>
      <child-type-B> ... </child-type-B>
      ...
   </child-type-C>


   ...

</root-node>

Я хочу преобразовать этот XML-файл в нечто, похожее на это:

--- after transformation ---
<root-node>

   <child-node> ... </child-node>
   <child-node> ... </child-node>
   <child-node> ... </child-node>
   <child-node>
      <child-node> ... </child-node>
      ...
   </child-node>

   ...

</root-node>

Фактически это означает, что структура документа остается прежней, но некоторые «выбранные» элементы переименовываются.Эти выбранные элементы начинаются с того же префикса (в этом примере с «child-type-»), но имеют различные суффиксы («A» | «B» | «C» | и т. Д.).

Почему все этонервотрепки?У меня есть программное обеспечение, которое требует ввода XML-файла.Для удобства я использую XML-схему, чтобы легко редактировать XML-файл, и эта схема помогает убедиться, что XML-файл будет правильным.К сожалению, XML-схемам не хватает некоторых аспектов чувствительности к контексту.Это приводит к тому, что XML-файл выглядит так, как показано в / перед преобразованием /.Программное обеспечение не может обработать такой xml-файл, поскольку ожидает файл, показанный в / после преобразования /.Таким образом, потребность в преобразовании.


Я хочу выполнить преобразование с помощью XSLT, и я уже понял, как это сделать.Мой подход состоял в том, чтобы определить правило для преобразования идентификаторов и одно правило для каждого элемента "child-type- *", который необходимо переименовать.Это решение работает, но оно не так уж и элегантно.В конечном итоге вы получаете множество правил.

--- sample transformation rules ---

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

<xsl:template match="child-type-A">
   <xsl:element name="child-node">
      <xsl:apply-templates select="@*|node()" />
   </xsl:element>
</xsl:template>

...

Есть ли способ сжать это только в два правила?Один для преобразования идентичности и один для всех элементов child-type- *?Может быть, используя XSLT в сочетании с некоторым регулярным выражением?Или вам нужно использовать другой подход для решения такой проблемы?

Ответы [ 4 ]

2 голосов
/ 20 мая 2011

(исправил мой ответ)

Этот фрагмент отлично работает с вашим примером XML. Я объединил два шаблона, потому что они оба хотят воздействовать на «все элементы». Мои предыдущие шаблоны не работали, потому что оба соответствовали одному и тому же выбору.

<xsl:template match="@*|node()">
    <xsl:choose>
        <xsl:when test="starts-with(name(), 'child-type')">
            <xsl:element name="child-node">
                <xsl:apply-templates select="@*|node()"/>
            </xsl:element>
        </xsl:when>
        <xsl:otherwise>
           <xsl:copy>
              <xsl:apply-templates select="@*|node()" />
           </xsl:copy>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

Учитывая ваш исходный XML:

<root-node>
   <child-type-A> ... </child-type-A>
   <child-type-A> ... </child-type-A>
   <child-type-B> ... </child-type-B>
   <child-type-C>
      <child-type-B> ... </child-type-B>
   </child-type-C>
</root-node>

Это приводит к следующему выводу:

<root-node>
<child-node> ... </child-node>
<child-node> ... </child-node>
<child-node> ... </child-node>
<child-node>
    <child-node> ... </child-node>
</child-node>
</root-node>
1 голос
/ 20 мая 2011

Не очень хорошая идея собирать информацию, придавая значение внутреннему синтаксису имени элемента (в крайнем случае, можно получить документ XML, в котором вся информация была получена от имени корневого элемента, <Surname_Kay.Firstname_Michael.Country_UK/>).Однако, если у вас есть данные в этой форме, их, безусловно, можно обработать, например, с помощью правила шаблона вида <xsl:template match="*[matches(name(), 'child-type-[A-Z]')]">

1 голос
/ 20 мая 2011

XSLtT имеет функцию начинается с , которая может использоваться для идентификации элементов, которые начинаются с 'child-type', что позволяет использовать одно совпадение шаблона. Смотрите этот связанный вопрос:

выберите элемент, который соответствует стартовому имени

0 голосов
/ 22 мая 2011

Вот общее преобразование XSLT 1.0 , которое может работать с параметрами, которые задают желаемые префиксы и, для каждого желаемого префикса, набор суффиксов, например, любое имя элемента с этим префиксом и один из эти суффиксы должны быть переименованы с желаемым новым именем:

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

 <my:renames>
  <rename prefix="child-type-"
          newVal="child-node">
    <suffix>A</suffix>
    <suffix>B</suffix>
    <suffix>C</suffix>
  </rename>
 </my:renames>

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

 <xsl:template match="/*//*">
  <xsl:choose>
  <xsl:when test=
   "document('')/*
         /my:renames
           /rename
             [@prefix[starts-with(name(current()),.)]
            and
              suffix
               [substring(name(current()),
                          string-length(name(current()))
                          - string-length(.) +1
                          )
               =
                 .
               ]
              ]
    ">

  <xsl:variable name="vNewName" select=
   "document('')/*
         /my:renames
           /rename
             [@prefix[starts-with(name(current()),.)]
            and
              suffix
               [substring(name(current()),
                          string-length(name(current()))
                          -string-length(.) +1
                          )
               =
                 .
               ]
              ]
              /@newVal
   "/>

      <xsl:element name="{$vNewName}">
       <xsl:apply-templates select="node()|@*"/>
      </xsl:element>
   </xsl:when>
   <xsl:otherwise>
    <xsl:call-template name="identity"/>
   </xsl:otherwise>
  </xsl:choose>
 </xsl:template>
</xsl:stylesheet>

При применении к предоставленному документу XML :

<root-node>
    <child-type-A> ... </child-type-A>
    <child-type-A> ... </child-type-A>
    <child-type-B> ... </child-type-B>
    <child-type-C>
      <child-type-B> ... </child-type-B>
      ...
    </child-type-C>
      ...
</root-node>

желаемый, правильный результат получается :

<root-node>
   <child-node> ... </child-node>
   <child-node> ... </child-node>
   <child-node> ... </child-node>
   <child-node>
      <child-node> ... </child-node>
      ...
    </child-node>
      ...
</root-node>

Заметьте : Используя это преобразование, вы можете переименовывать одновременно разные элементы с разными префиксами и соответствующими суффиксами, указанными в качестве внешних параметров / документов.

II. Эквивалентное решение XSLT 2.0:

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

 <xsl:variable name="vRules">
  <rule prefix="^child\-type\-" newVal="child-node">
    <suffix>A$</suffix>
    <suffix>B$</suffix>
    <suffix>C$</suffix>
  </rule>
 </xsl:variable>

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

 <xsl:template match=
  "*[for $n in name(.),
         $r in $vRules/*
                 [matches($n, @prefix)], 
         $s in $vRules/*/suffix
                 [matches($n, .)]
      return $r and $s
    ]">

    <xsl:variable name="vN" select="name()"/>

    <xsl:variable name="vNewName" select=
     "$vRules/*
           [matches($vN, @prefix)
           and 
            suffix[matches($vN, .)]
           ]
           /@newVal
     "/>
   <xsl:element name="{$vNewName}">
    <xsl:apply-templates select="node()|@*"/>
   </xsl:element>
 </xsl:template>
</xsl:stylesheet>

при применении к тому же XML-документу (см. Выше), опять то же самое, получается правильный вывод.

...