xml xslt transform - PullRequest
       1

xml xslt transform

2 голосов
/ 26 сентября 2011

Я пытался преобразовать какой-то простой XML в другой простой XML с помощью XSLT. Я новичок в XSLT, поэтому, если кто-то может дать мне пример, я остановлюсь на нем.

У меня есть произвольные файлы XML: например,

<element>
 <child_element>
  <grandchild_element>
     only one
  </grandchild_element>
 </child_element>
 <child_element>
  <grandchild_element>
     one
  </grandchild_element>
  <grandchild_element>
     two
  </grandchild_element>
 </child_element>
</element>

Из которого я хочу произвести:

<tree>
 <item class="element" id="1">
  <item class="child_element" id="11">
   <item class="grandchild_element" id="111" value="only one"/>
  </item>
  <item class="child_element" id="12">
   <item class="grandchild_element" id="121" value="only one"/>
   <item class="grandchild_element" id="122" value="only one"/>
  </item>
 </item>
</tree>

Спасибо!

Ответы [ 3 ]

0 голосов
/ 26 сентября 2011

Чтобы получить id для каждого элемента, вам нужно оглянуться назад на каждого из предков, и для каждого уровня подсчитать количество предшествующих братьев и сестер.

<xsl:attribute name="id">
    <xsl:apply-templates select="ancestor-or-self::*" mode="id"/>
</xsl:attribute>

<xsl:template match="*" mode="id">
   <xsl:value-of select="count(preceding-sibling::*) + 1"/>
</xsl:template>

Преобразовать имя элемента в атрибут class просто и так:

<xsl:attribute name="class">
   <xsl:value-of select="local-name()"/>
</xsl:attribute>

И преобразовать текст в атрибут value также довольно просто.

<xsl:template match="text()">
   <xsl:attribute name="value">
      <xsl:value-of select="normalize-space(.)"/>
   </xsl:attribute>
</xsl:template>

normalize-space здесь для удаления разрывов строк, показанных в вашем примере XML.

Вот полный XSLT

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

   <!-- Match root element -->
   <xsl:template match="/">
      <tree>
         <xsl:apply-templates select="node()"/>
      </tree>
   </xsl:template>

   <!-- Match any element in the XML -->
   <xsl:template match="*">
      <item>
         <xsl:attribute name="id">
            <xsl:apply-templates select="ancestor-or-self::*" mode="id"/>
         </xsl:attribute>
         <xsl:attribute name="class">
            <xsl:value-of select="local-name()"/>
         </xsl:attribute>
         <xsl:apply-templates select="@*|node()"/>
      </item>
   </xsl:template>

   <!-- Used to match ancestors to work out the id -->
   <xsl:template match="*" mode="id">
      <xsl:value-of select="count(preceding-sibling::*) + 1"/>
   </xsl:template>

   <!-- Convert text into the value attribute -->
   <xsl:template match="text()">
      <xsl:attribute name="value">
         <xsl:value-of select="normalize-space(.)"/>
      </xsl:attribute>
   </xsl:template>

   <!-- Copy any existing attributes in the XML -->
   <xsl:template match="@*">
      <xsl:copy/>
   </xsl:template>
</xsl:stylesheet>

При применении к вашему образцу XML выводится следующее:

<tree>
   <item id="1" class="element">
      <item id="11" class="child_element">
         <item id="111" class="grandchild_element" value="only one"/>
      </item>
      <item id="12" class="child_element">
         <item id="121" class="grandchild_element" value="one"/>
         <item id="122" class="grandchild_element" value="two"/>
      </item>
   </item>
</tree>
0 голосов
/ 27 сентября 2011

Одно из самых простых / кратчайших решений (только 3 шаблона, без режимов, без осей, без count(), только один xsl:attribute), которое в то же время является наиболее общим (работает с любыми именами элементов)

<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:template match="/">
  <tree>
   <xsl:apply-templates/>
  </tree>
 </xsl:template>

 <xsl:template match="*">
  <xsl:variable name="vNumber">
   <xsl:number count="*" level="multiple"/>
  </xsl:variable>

  <item class="{name()}"
        id="{translate($vNumber, '.', '')}">
   <xsl:apply-templates/>
  </item>
 </xsl:template>

 <xsl:template match="text()">
  <xsl:attribute name="value">
   <xsl:value-of select="normalize-space()"/>
  </xsl:attribute>
 </xsl:template>
</xsl:stylesheet>

когда это преобразование применяется к предоставленному документу XML :

<element>
 <child_element>
  <grandchild_element>
     only one
  </grandchild_element>
 </child_element>
 <child_element>
  <grandchild_element>
     one
  </grandchild_element>
  <grandchild_element>
     two
  </grandchild_element>
 </child_element>
</element>

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

<tree>
   <item class="element" id="1">
      <item class="child_element" id="11">
         <item class="grandchild_element" id="111" value="only one"/>
      </item>
      <item class="child_element" id="12">
         <item class="grandchild_element" id="121" value="one"/>
         <item class="grandchild_element" id="122" value="two"/>
      </item>
   </item>
</tree>
0 голосов
/ 26 сентября 2011

Вы бы написали шаблон для каждого элемента, например:

<xsl:template match="child_element">

и использовали счетчик предшествующих или предшествующих элементов, чтобы получить поле "id":

<xsl:template match="child_element">
    <item>
        <xsl:attribute name="id">
            <xsl:value-of select="concat(count(preceding-sibling::element),count(preceding::child_element)+1)"/>
        </xsl:attribute>
    </item>
</xsl:template>

Для идентификатора внука вам придется поиграться с .. и предшествующим братом или сестрой.Однако я настоятельно рекомендую вам в любом случае не использовать вашу текущую схему подсчета для идентификаторов: если у вас есть более 10 узлов на любом уровне, возникают коллизии.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...