Пользовательская функция XSLT, возвращающая набор узлов или фрагмент XML (не простой тип данных) - PullRequest
4 голосов
/ 31 декабря 2011

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

Входной документ:

<root>
<!--
 author: blablabla
 usage: more blablabla
 labelC: [in=2] <b>formatted</b> blablabla
-->
<tag1 name="first">
    <tag2>content a</tag2>
    <tag2>content b</tag2>
    <tag3 attrib="val">content c</tag3>
</tag1>

<!--
 author: blebleble
 usage: more blebleble
 labelC: blebleble
-->
<tag1 name="second">
    <tag2>content x</tag2>
    <tag2>content y</tag2>
    <tag3 attrib="val">content z</tag3>
</tag1>
</root>

Так что шаблон XSLTнапример:

    <xsl:template match="//tag1/preceding::comment()[1]" xmlns:d="java:com.dummy.func">
    <section>
     <para>
      <xsl:value-of select="d:genDoc(.)"/>
     </para>
    </section>
    </xsl:template>

Будет выдано:

    <section>
     <para>
      <author>blablabla</author>
      <usage>more blablabla</usage>
      <labelC in="2"><b>formatted</b> blablabla</labelC>
     </para>
    </section>

При сопоставлении при первом появлении tag1 и

    <section>
     <para>
      <author>blebleble</author>
      <usage>more blebleble</usage>
      <labelC>blebleble</labelC>
     </para>
    </section>

При сопоставлении при втором появлении.

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

Я нашел несколько примеров в Интернете, один в: http://cafeconleche.org/books/xmljava/chapters/ch17s03.html

В соответствии с примером, моя функция должна возвращать одно из следующих значений:

org.w3c.dom.traversal.NodeIterator,
org.apache.xml.dtm.DTM,
org.apache.xml.dtm.DTMAxisIterator,
org.apache.xml.dtm.DTMIterator,
org.w3c.dom.Node and its subtypes (Element, Attr, etc),
org.w3c.dom.DocumentFragment

Мне удалось реализовать функцию, возвращающую XML как простой тип String.Это, однако, создает несколько других проблем: главная из них заключается в том, что символы маркеров экранируются при вставке в исходный XML.

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

1 Ответ

1 голос
/ 07 марта 2012

Ниже приведен длинный путь, по которому вы хотите идти. Обратите внимание, что для этого требуется версия XSLT 2.0 (в XSLT 1.0 это также будет возможно при предоставлении функции замены для tokenize). Также обратите внимание, что это предполагает определенную структуру содержимого комментария.
Пояснение: комментарии сначала разбиваются на строки (разделитель & #xD;, который является переводом строки), затем в теге + значение (разделитель ":", разделение на автора, использование, labelC, порядок здесь не важен), затем в атрибутах и ​​значениях для labelC (разделитель "]", распознающих атрибуты как начинающиеся с "[").
Обратите внимание, что большая часть очистки пробелов выполняется с помощью normalize-space().

Отредактировано: версия xslt с функцией см. Внизу

1010 * XSLT *

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

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

    <xsl:template match="tag1/*">
    </xsl:template>

    <xsl:template match="comment()">
        <section>
            <para>
                <xsl:for-each select="tokenize(., '&#xD;')[string-length() != 0]">
                    <xsl:variable name="splitup" select="tokenize(normalize-space(current()), ':')"/>
                    <xsl:choose>
                        <xsl:when test="$splitup[1]='author'">
                            <author><xsl:value-of select="normalize-space($splitup[2])"/></author>
                        </xsl:when>
                        <xsl:when test="$splitup[1]='usage'">
                            <usage><xsl:value-of select="normalize-space($splitup[2])"/></usage>
                        </xsl:when>
                        <xsl:when test="$splitup[1]='labelC'">
                            <labelC>
                                <xsl:for-each select="tokenize($splitup[2], '] ')[string-length() != 0]">
                                    <xsl:variable name="labelCpart" select="normalize-space(current())"/>
                                    <xsl:choose>
                                        <xsl:when test="substring($labelCpart, 1,1) = '['">
                                            <xsl:variable name="attr" select="tokenize(substring($labelCpart, 2), '=')"/>
                                            <xsl:attribute name="{$attr[1]}"><xsl:value-of select="$attr[2]"/></xsl:attribute>
                                        </xsl:when>
                                        <xsl:otherwise>
                                            <xsl:value-of select="$labelCpart"/>
                                        </xsl:otherwise>
                                    </xsl:choose>
                                </xsl:for-each>
                            </labelC>
                        </xsl:when>
                    </xsl:choose>
                </xsl:for-each>
            </para>
        </section>
    </xsl:template>

</xsl:stylesheet>

применительно к следующему XML

<?xml version="1.0" encoding="UTF-8"?>
<root>
<!--
 author: blablabla
 usage: more blablabla
 labelC: [in=2] <b>formatted</b> blablabla
-->
<tag1 name="first">
    <tag2>content a</tag2>
    <tag2>content b</tag2>
    <tag3 attrib="val">content c</tag3>
</tag1>

<!--
 author: blebleble
 usage: more blebleble
 labelC: blebleble
-->
<tag1 name="second">
    <tag2>content x</tag2>
    <tag2>content y</tag2>
    <tag3 attrib="val">content z</tag3>
</tag1>
</root>

дает следующий вывод

<?xml version="1.0" encoding="UTF-8"?>
<output>
    <section>
        <para>
            <author>blablabla</author>
            <usage>more blablabla</usage>
            <labelC in="2">&lt;b&gt;formatted&lt;/b&gt; blablabla</labelC>
        </para>
    </section>
    <section>
        <para>
            <author>blebleble</author>
            <usage>more blebleble</usage>
            <labelC>blebleble</labelC>
        </para>
    </section>
</output>

EDITED xslt с вызовом функции (выдает тот же вывод)

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:d="java:com.dummy.func"
exclude-result-prefixes="d">

    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

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

    <xsl:template match="tag1/*">
    </xsl:template>

    <xsl:function name="d:section">
        <xsl:param name="comm"/>
        <section>
            <para>
                <xsl:for-each select="tokenize($comm, '&#xD;')[string-length() != 0]">
                    <xsl:variable name="splitup" select="tokenize(normalize-space(current()), ':')"/>
                    <xsl:choose>
                        <xsl:when test="$splitup[1]='author'">
                            <author><xsl:value-of select="normalize-space($splitup[2])"/></author>
                        </xsl:when>
                        <xsl:when test="$splitup[1]='usage'">
                            <usage><xsl:value-of select="normalize-space($splitup[2])"/></usage>
                        </xsl:when>
                        <xsl:when test="$splitup[1]='labelC'">
                            <labelC>
                                <xsl:for-each select="tokenize($splitup[2], '] ')[string-length() != 0]">
                                    <xsl:variable name="labelCpart" select="normalize-space(current())"/>
                                    <xsl:choose>
                                        <xsl:when test="substring($labelCpart, 1,1) = '['">
                                            <xsl:variable name="attr" select="tokenize(substring($labelCpart, 2), '=')"/>
                                            <xsl:attribute name="{$attr[1]}"><xsl:value-of select="$attr[2]"/></xsl:attribute>
                                        </xsl:when>
                                        <xsl:otherwise>
                                            <xsl:value-of select="$labelCpart"/>
                                        </xsl:otherwise>
                                    </xsl:choose>
                                </xsl:for-each>
                            </labelC>
                        </xsl:when>
                    </xsl:choose>
                </xsl:for-each>
            </para>
        </section>
    </xsl:function>

    <xsl:template match="comment()">
        <xsl:copy-of select="d:section(.)"/>
    </xsl:template>

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