как выбрать определенные наборы узлов в xslt

Xslt, который я сейчас использую, генерирует все теги в корне. Мне нужно получить наборы <row> и набор <config>.

Исходный XML:

<root>
    <postdate>2011-03-30</postdate>
    <location>84</location>
    <meal>07:36</meal>
    <config>
        <postdate>2011-03-30</postdate>
        <location>84</location>
        <meal>07:36</meal>
        <checknumber>91339082011-03-30T07:36:12</checknumber>
    </config>
    <items>
        <row>
            <descriptor>7297364</descriptor>
            <qty>1</qty>
            <price>33</price>
            <value>33</value>
            <recordtype>1</recordtype>
            <postdate>2011-03-30</postdate>
            <location>84</location>
        </row>
        <row>
            <descriptor>7794473</descriptor>
            <qty>1</qty>
            <price>60</price>
            <value>60</value>
            <recordtype>1</recordtype>
            <postdate>2011-03-30</postdate>
            <location>84</location>
        </row>
    </items>
    <tenders>
        <row>
            <id>13</id>
            <value>117.99</value>
            <recordtype>2</recordtype>
            <postdate>2011-03-30</postdate>
            <location>84</location>
        </row>
    </tenders>
    <taxes>
        <row>
            <id>2</id>
            <value>8.25</value>
            <recordtype>3</recordtype>
            <postdate>2011-03-30</postdate>
            <location>84</location>
        </row>
    </taxes>
</root>

Попытка Xslt:

<xsl:stylesheet version="1.0" exclude-result-prefixes="msxsl" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt">
    <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
    <xsl:template match="row/*">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

Желаемый результат:

<root>
    <config>
        <postdate>2011-03-30</postdate>
        <location>84</location>
        <meal>07:36</meal>
        <checknumber>91339082011-03-30T07:36:12</checknumber>
    </config>
    <row>
        <descriptor>7297364</descriptor>
        <qty>1</qty>
        <price>33</price>
        <value>33</value>
        <recordtype>1</recordtype>
        <postdate>2011-03-30</postdate>
        <location>84</location>
    </row>
    <row>
        <descriptor>7794473</descriptor>
        <qty>1</qty>
        <price>60</price>
        <value>60</value>
        <recordtype>1</recordtype>
        <postdate>2011-03-30</postdate>
        <location>84</location>
    </row>
    <row>
        <id>13</id>
        <value>117.99</value>
        <recordtype>2</recordtype>
        <postdate>2011-03-30</postdate>
        <location>84</location>
    </row>
    <row>
        <id>2</id>
        <value>8.25</value>
        <recordtype>3</recordtype>
        <postdate>2011-03-30</postdate>
        <location>84</location>
    </row>
</root>

person GaneshT    schedule 27.11.2012    source источник
comment
сначала используйте xpath для соответствия элементу конфигурации, а внутри него сделайте еще один xpath для соответствия конфигурации, т. е. первый match=/root/config второй должен быть просто match=/items/config. xslt не моя сумка (я довольно регулярно использую запросы С# и xpath, и они кажутся мне «примерно» правильными, хотя вы использовали подстановочный знак (?), т. чтобы указать корень и перейти к узлу ИЛИ вы можете использовать // в xpath для обозначения ВЕЗДЕ в документе.   -  person Paul Sullivan    schedule 28.11.2012
comment
xpath - это то, что xslt использует для обхода дерева, поэтому почитайте его (это очень просто)   -  person Paul Sullivan    schedule 28.11.2012


Ответы (2)


Это короткое и простое преобразование:

<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="node()|@*">
     <xsl:copy>
       <xsl:apply-templates select="node()|@*"/>
     </xsl:copy>
 </xsl:template>

 <xsl:template match=
  "node()[not(self::root or ancestor-or-self::config or ancestor-or-self::row)]">
  <xsl:apply-templates/>
 </xsl:template>
</xsl:stylesheet>

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

<root>
    <postdate>2011-03-30</postdate>
    <location>84</location>
    <meal>07:36</meal>
    <config>
        <postdate>2011-03-30</postdate>
        <location>84</location>
        <meal>07:36</meal>
        <checknumber>91339082011-03-30T07:36:12</checknumber>
    </config>
    <items>
        <row>
            <descriptor>7297364</descriptor>
            <qty>1</qty>
            <price>33</price>
            <value>33</value>
            <recordtype>1</recordtype>
            <postdate>2011-03-30</postdate>
            <location>84</location>
        </row>
        <row>
            <descriptor>7794473</descriptor>
            <qty>1</qty>
            <price>60</price>
            <value>60</value>
            <recordtype>1</recordtype>
            <postdate>2011-03-30</postdate>
            <location>84</location>
        </row>
    </items>
    <tenders>
        <row>
            <id>13</id>
            <value>117.99</value>
            <recordtype>2</recordtype>
            <postdate>2011-03-30</postdate>
            <location>84</location>
        </row>
    </tenders>
    <taxes>
        <row>
            <id>2</id>
            <value>8.25</value>
            <recordtype>3</recordtype>
            <postdate>2011-03-30</postdate>
            <location>84</location>
        </row>
    </taxes>
</root>

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

<root>
   <config>
      <postdate>2011-03-30</postdate>
      <location>84</location>
      <meal>07:36</meal>
      <checknumber>91339082011-03-30T07:36:12</checknumber>
   </config>
   <row>
      <descriptor>7297364</descriptor>
      <qty>1</qty>
      <price>33</price>
      <value>33</value>
      <recordtype>1</recordtype>
      <postdate>2011-03-30</postdate>
      <location>84</location>
   </row>
   <row>
      <descriptor>7794473</descriptor>
      <qty>1</qty>
      <price>60</price>
      <value>60</value>
      <recordtype>1</recordtype>
      <postdate>2011-03-30</postdate>
      <location>84</location>
   </row>
   <row>
      <id>13</id>
      <value>117.99</value>
      <recordtype>2</recordtype>
      <postdate>2011-03-30</postdate>
      <location>84</location>
   </row>
   <row>
      <id>2</id>
      <value>8.25</value>
      <recordtype>3</recordtype>
      <postdate>2011-03-30</postdate>
      <location>84</location>
   </row>
</root>

Пояснение:

  1. Использование и переопределение правила идентификации.

  2. Правильное использование оси ancestor-or-self::.

person Dimitre Novatchev    schedule 28.11.2012
comment
Объяснение того, как это работает, было бы здорово. Сейчас это решение не очень полезно для тех, кто пытается добиться похожих, но разных результатов. - person Daniil Shevelev; 07.02.2018
comment
@DaniilShevelev, объяснение из двух пунктов в этом ответе относится к фундаментальным концепциям XSLT и XPath, которые хорошо известны XSLT-программистам. Это хорошо объяснено в хороших книгах (@MichaelKay) и учебных курсах Pluralsight — на многих страницах и часах видео. Это было бы нецелесообразно -- и никто не позволит их воспроизвести здесь. Я рекомендую прочитать такую ​​хорошую книгу и/или посмотреть обучающий курс Pluralsight по XSLT. - person Dimitre Novatchev; 08.02.2018

Я понял это. Этот xslt работает для меня.

<xsl:stylesheet version="1.0" exclude-result-prefixes="msxsl" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt">
    <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
    <xsl:template match="/">
        <root>
            <xsl:for-each select="//row">
                <row>
                    <xsl:apply-templates/>
                </row>
            </xsl:for-each>
            <xsl:for-each select="//config">
                <config>
                    <xsl:apply-templates/>
                </config>
            </xsl:for-each>
        </root>
    </xsl:template>
    <xsl:template match="*">
        <xsl:element name="{name()}">
            <xsl:value-of select="."/>
        </xsl:element>
    </xsl:template>
</xsl:stylesheet>
person GaneshT    schedule 27.11.2012
comment
Есть гораздо более простое и короткое решение (см. мой ответ). Вам нужно привыкнуть к использованию и переопределению шаблона удостоверения — это один из самых фундаментальных шаблонов проектирования XSLT. - person Dimitre Novatchev; 28.11.2012