基于相同XML的其他内容递归更改XML输出

时间:2016-06-16 13:18:08

标签: xml xslt

给出以下XML:

<?xml version="1.0" encoding="utf-8"?>
<BMECAT version="1.2">
    <T_NEW_CATALOG>
        <CATALOG_GROUP_SYSTEM>
            <CATALOG_STRUCTURE type="root">
                <GROUP_ID>1</GROUP_ID>
                <PARENT_ID>0</PARENT_ID>
            </CATALOG_STRUCTURE>
            <CATALOG_STRUCTURE type="node">
                <GROUP_ID>2</GROUP_ID>
                <PARENT_ID>1</PARENT_ID>
            </CATALOG_STRUCTURE>
            <CATALOG_STRUCTURE type="node">
                <GROUP_ID>3</GROUP_ID>
                <PARENT_ID>1</PARENT_ID>
            </CATALOG_STRUCTURE>
            <CATALOG_STRUCTURE type="leaf">
                <GROUP_ID>4</GROUP_ID>
                <PARENT_ID>2</PARENT_ID>
            </CATALOG_STRUCTURE>
            <CATALOG_STRUCTURE type="leaf">
                <GROUP_ID>5</GROUP_ID>
                <PARENT_ID>3</PARENT_ID>
            </CATALOG_STRUCTURE>
        </CATALOG_GROUP_SYSTEM>
        <ARTICLE_TO_CATALOGGROUP_MAP>
            <ART_ID>ART1</ART_ID>
            <CATALOG_GROUP_ID>5</CATALOG_GROUP_ID>
        </ARTICLE_TO_CATALOGGROUP_MAP>
    </T_NEW_CATALOG>
</BMECAT>

我需要将属性inUse=true设置为CATALOG_STRUCTURE s(以及它们在PARENT_ID元素中引用的元素),这些属性在任何ARTICLE_TO_CATALOGGROUP_MAP/CATALOG_GROUP_ID元素中引用。

我无法完全理解如何根据文档另一部分中的内容操作部分文档,特别是如果该部分基本上是一棵树但显示为扁平化。

首先我创建了这个,但却一直在编写markGroup模板......

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xslt="http://xml.apache.org/xslt" exclude-result-prefixes="#all">
    <xsl:template match="*">
        <xsl:copy>
            <xsl:apply-templates/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*"/>
            <xsl:apply-templates/>
        </xsl:copy>
    </xsl:template>

    <xsl:template name="isUsed">
        <xsl:param name="groupId" />
        <xsl:if test="//CATALOG_GROUP_ID/text() = $groupId">true</xsl:if>
    </xsl:template>

    <xsl:template match="CATALOG_STRUCTURE">
        <xsl:variable name="groupId" select="GROUP_ID/text()"/>
        <xsl:variable name="isUsedDirectly">
            <xsl:call-template name="isUsed">
                <xsl:with-param name="groupId" select="$groupId"/>
            </xsl:call-template>
        </xsl:variable>
        <xsl:element name="CATALOG_STRUCTURE">
            <xsl:if test="$isUsedDirectly = 'true'">
                <xsl:attribute name="inUse">true</xsl:attribute>
            </xsl:if>
            <xsl:apply-templates select="@*"/>
            <xsl:apply-templates/>
        </xsl:element>
    </xsl:template>
</xsl:stylesheet>

但是这只会选择“直接”组,并且不会将父项标记为应该使用它。

此示例的所需输出为:

<?xml version="1.0" encoding="utf-8"?>
<BMECAT version="1.2">
    <T_NEW_CATALOG>
        <CATALOG_GROUP_SYSTEM>
            <CATALOG_STRUCTURE type="root" inUse="true">
                <GROUP_ID>1</GROUP_ID>
                <PARENT_ID>0</PARENT_ID>
            </CATALOG_STRUCTURE>
            <CATALOG_STRUCTURE type="node">
                <GROUP_ID>2</GROUP_ID>
                <PARENT_ID>1</PARENT_ID>
            </CATALOG_STRUCTURE>
            <CATALOG_STRUCTURE type="node" inUse="true">
                <GROUP_ID>3</GROUP_ID>
                <PARENT_ID>1</PARENT_ID>
            </CATALOG_STRUCTURE>
            <CATALOG_STRUCTURE type="leaf">
                <GROUP_ID>4</GROUP_ID>
                <PARENT_ID>2</PARENT_ID>
            </CATALOG_STRUCTURE>
            <CATALOG_STRUCTURE type="leaf" inUse="true">
                <GROUP_ID>5</GROUP_ID>
                <PARENT_ID>3</PARENT_ID>
            </CATALOG_STRUCTURE>
        </CATALOG_GROUP_SYSTEM>
        <ARTICLE_TO_CATALOGGROUP_MAP>
            <ART_ID>ART1</ART_ID>
            <CATALOG_GROUP_ID>5</CATALOG_GROUP_ID>
        </ARTICLE_TO_CATALOGGROUP_MAP>
    </T_NEW_CATALOG>
</BMECAT>

1 个答案:

答案 0 :(得分:1)

我会定义一个键和一个递归函数来遍历引用:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:mf="http://example.com/mf"
    exclude-result-prefixes="xs mf"
    version="2.0">

    <xsl:key name="ref" match="CATALOG_STRUCTURE" use="GROUP_ID"/>

    <xsl:function name="mf:get-refs" as="element(CATALOG_STRUCTURE)*">
        <xsl:param name="ids" as="xs:string*"/>
        <xsl:variable name="refs" as="element(CATALOG_STRUCTURE)*" select="key('ref', $ids, $catalog)"/>
        <xsl:sequence select="$refs, if (exists($refs)) then mf:get-refs($refs/PARENT_ID) else ()"/>
    </xsl:function>

    <xsl:variable name="catalog" select="/BMECAT/T_NEW_CATALOG/CATALOG_GROUP_SYSTEM"/>

    <xsl:variable name="referenced" as="element(CATALOG_STRUCTURE)*" select="mf:get-refs(//ARTICLE_TO_CATALOGGROUP_MAP/CATALOG_GROUP_ID)"/>

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

    <xsl:template match="CATALOG_STRUCTURE[some $ref in $referenced satisfies $ref is current()]">
        <xsl:copy>
            <xsl:attribute name="InUse" select="'true'"/>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

假设XSLT 2.0(或更高版本)处理器如Saxon 9或AltovaXML或XmlPrime或Exselt。

关于它是如何工作的,密钥允许我们通过CATALOG_STRUCTURE引用GROUP_ID元素,然后递归函数允许我们计算所有递归引用元素的序列。为了全部识别它们,变量referenced全部选择它们,现在我们只需要为那些根据需要添加属性的元素设置模板,以实现模式CATALOG_STRUCTURE[some $ref in $referenced satisfies $ref is current()]检查匹配的节点是其中一个$referenced元素。

如果您可以访问像Saxon 9.6或9.7的商业版本这样的XSLT 3.0处理器,您甚至可以编写一个只采用变量referenced的模式:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:mf="http://example.com/mf"
    exclude-result-prefixes="xs mf"
    version="3.0">

    <xsl:key name="ref" match="CATALOG_STRUCTURE" use="GROUP_ID"/>

    <xsl:function name="mf:get-refs" as="element(CATALOG_STRUCTURE)*">
        <xsl:param name="ids" as="xs:string*"/>
        <xsl:variable name="refs" as="element(CATALOG_STRUCTURE)*" select="key('ref', $ids, $catalog)"/>
        <xsl:sequence select="$refs, if (exists($refs)) then mf:get-refs($refs/PARENT_ID) else ()"/>
    </xsl:function>

    <xsl:variable name="catalog" select="/BMECAT/T_NEW_CATALOG/CATALOG_GROUP_SYSTEM"/>

    <xsl:variable name="referenced" as="element(CATALOG_STRUCTURE)*" select="mf:get-refs(//ARTICLE_TO_CATALOGGROUP_MAP/CATALOG_GROUP_ID)"/>

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

    <xsl:template match="$referenced">
        <xsl:copy>
            <xsl:attribute name="InUse" select="'true'"/>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>