Как я могу объединить все эти XML-теги в один больший тег с тем же именем с помощью XSLT?


Я преобразую этот XML:

<root>
  <contrib contrib-type="author">
    <name>
      <last-name>Kottke</last-name>
      <first-name>Leo</first-name>
    </name>
  </contrib>

  <contrib contrib-type="author">
    <name>
      <last-name>McKee</last-name>
      <first-name>Andy</first-name>
    </name>
  </contrib>

  <contrib contrib-type="author">
    <name>
      <last-name>Hedges</last-name>
      <first-name>Michael</first-name>
    </name>
  </contrib>
</root>

...с этим XSL:

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

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

        <!-- Authors -->
        <xsl:template match="contrib[@contrib-type='author']">
            <Authors>
              <xsl:if test="position() != last()">
                <xsl:value-of select = "concat(name/first-name, ' ', name/last-name, ', ')" />
              </xsl:if>

              <xsl:if test="position() = last()">
                <xsl:value-of select = "concat('and ', name/first-name, ' ', name/last-name, '.')" />
              </xsl:if>
            </Authors>
        </xsl:template>
</xsl:stylesheet>

... и я получаю этот вывод:

<root>
    <Authors>Leo Kottke, </Authors>
    <Authors>Andy McKee, </Authors>
    <Authors>and Michael Hedges.</Authors>
</root>





Однако я пытаюсь объединить все теги Author в один тег большего размера Author, чтобы результат выглядел как это:

<root>
    <Authors>Leo Kottke, Andy McKee, and Michael Hedges.</Authors>
</root>


Нужно ли каким-то образом использовать цикл for-each?


person Ian Campbell    schedule 08.08.2012    source источник


Ответы (2)


xsl:for-each не требуется. Просто добавьте шаблон для /* (или /root) и удалите Authors из совпадения contrib:

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

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

    <xsl:template match="/root">
        <xsl:copy>
            <xsl:apply-templates select="@*"/>
            <Authors>
                <xsl:apply-templates select="contrib[@contrib-type='author']"/>
            </Authors>
        </xsl:copy>
    </xsl:template>

    <!-- Authors -->
    <xsl:template match="contrib[@contrib-type='author']">
        <xsl:if test="position() != last()">
            <xsl:value-of select = "concat(name/first-name, ' ', name/last-name, ', ')" />
        </xsl:if>

        <xsl:if test="position() = last()">
            <xsl:value-of select = "concat('and ', name/first-name, ' ', name/last-name, '.')" />
        </xsl:if>
    </xsl:template>
</xsl:stylesheet>
person Daniel Haley    schedule 08.08.2012
comment
Спасибо @DevNull за помощь! Однако у меня есть много других таких тегов для преобразования, кроме Authors, и добавление этого кода к остальной его части конфликтует и приводит к выводу только Authors... - person Ian Campbell; 09.08.2012
comment
А, значит, я изменил <xsl:apply-templates select="@*"/> на <xsl:apply-templates select="node()"/>, и теперь все работает нормально. Я не совсем уверен, что я там понял, но кажется, что применение преобразования Authors ко всем атрибутам аннулирует все узлы, которые не соответствуют условию. Еще раз спасибо @DevNull! - person Ian Campbell; 09.08.2012

Что-то немного проще

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

    <xsl:template match="/root">
        <xsl:variable name="authors">
            <xsl:for-each select="contrib/name">
                <xsl:text>, </xsl:text>
                <xsl:value-of select="first-name"/>
                <xsl:text> </xsl:text>
                <xsl:value-of select="last-name"/>
            </xsl:for-each>
        </xsl:variable>

        <root>
            <authors><xsl:value-of select="substring($authors,3)"/></authors>
        </root>
    </xsl:template>

</xsl:stylesheet>
person Jim Garrison    schedule 08.08.2012
comment
Спасибо @Jim за помощь! Однако количество имен неизвестного размера (не всегда только 3), и положение элемента <contrib contrib-type="author"> будет меняться по отношению к корню... - person Ian Campbell; 09.08.2012
comment
В моем ответе нет ничего, что ограничивало бы что-либо до 3 вхождений. Что касается различной глубины, просто измените выбор на .//contrib/name. - person Jim Garrison; 09.08.2012
comment
Ах, извините, я неправильно понял строку <xsl:value-of select="substring($authors,3)"/>, и еще раз спасибо за информацию... так что же означает это утверждение? Я получил ожидаемые результаты при тестировании на xsltcake.com, однако без окончательного и и периода. - person Ian Campbell; 09.08.2012
comment
Вместо того, чтобы условно добавлять разделитель ", ", предыдущий код ВСЕГДА добавляет его перед каждым именем, а затем в конце удаляется первый. Эта подстрока() извлекается из переменной, начиная с позиции 3, поскольку позиции 1-2 содержат , . - person Jim Garrison; 09.08.2012