XSLT 1.0: переставлять xml разными шагами (xsl-template) - PullRequest
0 голосов
/ 04 июля 2018

Я пытаюсь переставить XML в несколько этапов, используя разные xsl-шаблоны, и мне удалось заставить его работать - с двумя отдельными преобразованиями xsl в последовательности. Я в настоящее время изо всех сил пытаюсь поместить 2 файла xsl в один, но потерпел неудачу. Для этого: есть ли способ использовать результат одного шаблона в другом шаблоне?

Исходный XML:

<order>
    <header/>
    <items>
        <item no="001" material="5001" qty="4"/>
        <item no="002" material="5002" qty="5"/>
        <item no="P000" material="box" qty="2"/>
        <item no="P001" origin_no="001" material="5001" batch="L01" qty="1"/>
        <item no="P002" origin_no="001" material="5001" batch="L02" qty="3"/>
    </items>
    <packages>
        <package id="U01">
            <content item_no="P001" qty="1"/>
            <content item_no="002" qty="2"/>
        </package>
        <package id="U02">
            <content item_no="P002" qty="3"/>
            <content item_no="002" qty="2"/>
        </package>
        <package id="U03">
            <content item_no="002" qty="1"/>
        </package>
    </packages>
    <summery/>
</order>

Этот XML содержит заказ на доставку с двумя позициями и пакетами доставки. 3. Строка с номером «P000» - это материал для помощи в упаковке. Позиции 4. и 5. - это две партии, отделенные от позиции 1.. Ссылка между обычной позицией и ее групповым разбиением является атрибутом origin_no.

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

Результат должен быть следующим:

<order>
    <header/>
    <items>
        <item no="001" material="5001" qty="4">
            <package id="U01" item_no="P001" qty="1" origin_item_no="001" batch="L01"/>
            <package id="U02" item_no="P002" qty="3" origin_item_no="001" batch="L02"/>
        </item>
        <item no="002" material="5002" qty="5">
            <package id="U01" item_no="002" qty="2"/>
            <package id="U02" item_no="002" qty="2"/>
            <package id="U03" item_no="002" qty="1"/>
        </item>
    </items>
    <summery/>
</order>

Запустив следующие 2 xsl в последовательности, я мог бы успешно получить результат. Мои вопросы будут такими: есть ли элегантный способ выполнить это требование в одном сопоставлении XSLT без использования расширения exsl: node-set (которое я узнал из других публикаций)?

Первый XSL:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" encoding="UTF-8" indent="yes"/>
    <xsl:template match="@* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="package/content">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
            <xsl:variable name="v_item_no_package" select="@item_no"/>
            <xsl:for-each select="../../../items/item[@no = $v_item_no_package]">
                <xsl:attribute name="origin_item_no"><xsl:value-of select="@origin_no"/></xsl:attribute>
                <xsl:attribute name="batch"><xsl:value-of select="@batch"/></xsl:attribute>
            </xsl:for-each>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

Второй XSL:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" encoding="UTF-8" indent="yes"/>
    <xsl:template match="@* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="item">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
            <xsl:variable name="v_no_item" select="@no"/>
            <xsl:for-each select="../../packages/package/content[@origin_item_no = $v_no_item]">
                <xsl:element name="package">
                    <xsl:copy-of select="../@id"/>
                    <xsl:copy-of select="./* | @*[not(.='')]"/>
                </xsl:element>
            </xsl:for-each>
            <xsl:for-each select="../../packages/package/content[@item_no = $v_no_item]">
                <xsl:element name="package">
                    <xsl:copy-of select="../@id"/>
                    <xsl:copy-of select="./* | @*[not(.='')]"/>
                </xsl:element>
            </xsl:for-each>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="packages">
    </xsl:template>
    <xsl:template match="items/item[starts-with(@no, 'P')]">
    </xsl:template>
</xsl:stylesheet>

Большое спасибо и поздравления.

1 Ответ

0 голосов
/ 05 июля 2018

Если вы хотите иметь два шага преобразования в одной таблице стилей, тогда вам нужно использовать режимы https://www.w3.org/TR/1999/REC-xslt-19991116#modes, и дополнительно с XSLT 1 вам нужно использовать функцию расширения, такую ​​как exsl:node-set, как результат одного шага преобразования является фрагментом дерева результатов, и чтобы пройти второй этап, вам нужно сначала преобразовать его в набор узлов. Таким образом, у вас есть один подход к этому, хотя по какой-то причине вы, кажется, хотите избежать exsl:node-set.

Если вы хотите реализовать свое требование по-другому, тогда вам нужно исключить добавление атрибутов в контент и вместо этого попытаться собрать и преобразовать их напрямую, дополнительно для обработки различных ссылок полезно использовать ключи :

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

    <xsl:output method="xml" encoding="UTF-8" indent="yes"/>
    <xsl:strip-space elements="*"/>

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

    <xsl:key name="content-ref" match="package/content" use="@item_no"/>
    <xsl:key name="p-item-ref" match="items/item" use="@origin_no"/>

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

            <xsl:apply-templates select="key('content-ref', @no)"/>

            <xsl:variable name="referenced-items" select="key('p-item-ref', @no)"/>
            <xsl:for-each select="$referenced-items">
                <xsl:apply-templates select="key('content-ref', @no)">
                    <xsl:with-param name="atts" select="@batch | @origin_no"/>
                </xsl:apply-templates>                 
            </xsl:for-each>

        </xsl:copy>
    </xsl:template>
    <xsl:template match="packages">
    </xsl:template>
    <xsl:template match="items/item[starts-with(@no, 'P')]">
    </xsl:template>

    <xsl:template match="package/content">
        <xsl:param name="atts" select="/.."/>
        <package id="{../@id}">
            <xsl:apply-templates select="@*[normalize-space()] | $atts"/>
        </package>
    </xsl:template>

    <xsl:template match="@origin_no">
        <xsl:attribute name="origin_item_no">
            <xsl:value-of select="."/>
        </xsl:attribute>
    </xsl:template>

</xsl:stylesheet>

Онлайн-образец на https://xsltfiddle.liberty -development.net / pPqsHTu / 3 , который, я думаю, справляется с работой.

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