Как правильно обращаться к элементам-предкам в XSLT? - PullRequest
0 голосов
/ 17 мая 2018

https://xsltfiddle.liberty -development.net / bFDb2C4

Я хочу преобразовать следующий XML в CSV:

<employees>
   <global>
       <attr>test</attr>
   </global>
   <employee>
      <details>
         <name>Joe</name>
         <age>34</age>
         <stage>
            <type code="A" count="1"/>
            <type code="B" count="2"/>
            <type code="C" count="3"/>
         </stage>
      </details>
   </employee>
   <employee>
     <details>
         <age>24</age>
         <name>Sam</name>
      </details>
      <stage>
        <type code="A" count="1"/>
      </stage>
   </employee>
</employees>

Результат должен быть:

test;Joe;34;A;1
test;Joe;34;B;2
test;Joe;34;C;3
test;Sam;24;A;1

Поэтому я подумал, что могу просто сопоставить самый глубокий уровень (здесь type) и добавить все ancestor::. Следующий xslt работает в целом, но также выдает много «шума» вокруг желаемого результата (см. Xsltfidde):

<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text" omit-xml-declaration="yes" indent="no"/> 
  <xsl:template match="type">
            <xsl:value-of select="ancestor::employees/global/attr,
                    ancestor::employee/details/name,
                    ancestor::employee/details/age,
                    @code,
                    @count"
                separator=";" />
    </xsl:template>
</xsl:stylesheet>

Вопрос: откуда берется «шум»? Как я могу просто получить линии CSV?

Это текущий результат:

 test



 Joe
 34

    test;Joe;34;A;1
    test;Joe;34;B;2
    test;Joe;34;C;3





 24
 Sam


test;Sam;24;A;1

1 Ответ

0 голосов
/ 17 мая 2018

Шаблон у вас в порядке, и делает свое дело. Проблема в том, что, когда XSLT начинает обработку, он ищет шаблон, соответствующий элементу документа /, которого нет в вашем шаблоне. Когда XSLT ищет шаблон, и в вашем шаблоне его нет, он использует встроенные шаблоны .

По сути, он будет перемещаться по всему XML и выводить текстовые узлы, где он его найдет. Таким образом, вы получаете много текстового вывода, прежде чем он, наконец, дойдет до вашего шаблона, соответствующего type.

Есть несколько решений. Одним из них является наличие шаблона, соответствующего /, и явное выделение type узлов

<xsl:template match="/">
  <xsl:apply-templates select="//type" />
</xsl:template>

Другой вариант - иметь шаблон, соответствующий узлам text(), чтобы игнорировать их, переопределяя поведение шаблона по умолчанию

 <xsl:template match="text()" />

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

<xsl:mode on-no-match="shallow-skip"/>

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

Попробуйте это XSLT

<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text" omit-xml-declaration="yes" indent="no"/> 

  <xsl:mode on-no-match="shallow-skip"/>

  <xsl:template match="type">
    <xsl:value-of select="ancestor::global/attr,
            ancestor::employee/details/name,
            ancestor::employee/details/age,
            @code,
            @count"
        separator=";" />
    <xsl:text>&#10;</xsl:text>
  </xsl:template>
</xsl:stylesheet>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...