复制引用的XML元素

时间:2018-05-08 14:51:54

标签: java xml xslt

我有一个源XML文档,看起来像这样:

<root>
    <users>
        <user id="1" name="user 1" />
        <user id="2" name="user 2" />
        <user id="3" name="user 3" />
    </users>
    <posts>
        <post>
            <user>1</user>
            <text>First sample post!</text>
            <status>DELETED</status>
        </post>
        <post>
            <user>2</user>
            <text>Second sample post!</text>
            <status>ACTIVE</status>
        </post>
        <post>
            <user>3</user>
            <text>Third sample post!</text>
            <status>DELETED</status>
        </post>
    </posts>
</root>

我需要过滤用户,以便目标文档只包含ACTIVE帖子和post元素中引用的那些用户。:

<root>
    <users>
        <user id="2" name="user 2" />
    </users>
    <posts>
        <post>
            <user>2</user>
            <text>Second sample post!</text>
        </post>
    </posts>
</root>

我无权更改源文档,我需要使用XSLT(我很新)来实现这一点。

我可以轻松过滤帖子,但我不确定如何建立用户列表。

在我走得更远之前,我想检查一下是否可行。

干杯

2 个答案:

答案 0 :(得分:1)

是的,可以使用这样的样式表:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <!-- Copy asnything not overridden below -->
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

    <!-- Copy <user> if active <post> exists -->
    <xsl:template match="users/user"><!-- don't match 'post/user' -->
        <xsl:variable name="userId" select="@id"/>
        <xsl:if test="../../posts/post[user = $userId][status = 'ACTIVE']">
            <xsl:copy>
                <xsl:apply-templates select="@*|node()"/>
            </xsl:copy>
        </xsl:if>
    </xsl:template>

    <!-- Copy <post> if active -->
    <xsl:template match="post">
        <xsl:if test="status = 'ACTIVE'">
            <xsl:copy>
                <xsl:apply-templates select="@*|node()"/>
            </xsl:copy>
        </xsl:if>
    </xsl:template>

    <!-- Don't copy <status> -->
    <xsl:template match="status">
    </xsl:template>

</xsl:stylesheet>

测试

TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer = factory.newTransformer(new StreamSource(new File("test.xslt")));
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
transformer.transform(new StreamSource(new File("test.xml")),
                      new StreamResult(System.out));

输出

<root>
    <users>

        <user id="2" name="user 2"/>

    </users>
    <posts>

        <post>
            <user>2</user>
            <text>Second sample post!</text>

        </post>

    </posts>
</root>

答案 1 :(得分:1)

首先,您应该了解身份模板

post

它本身将完全从源文档复制到所有节点。

这意味着,不是根据您需要复制的内容进行思考,而是根据您不需要复制的内容进行思考。这是通过添加覆盖身份模板的具有更高优先级的模板来实现的。

您不希望status元素在<xsl:template match="post[status!='ACTIVE']" /> 不是“有效”的情况下?只需要一个空模板来阻止它们被复制。

status

同样,要删除<xsl:template match="status" /> 节点本身(对于它复制的帖子)

user

对于xsl:key元素,请考虑使用post查找<xsl:key name="posts" match="post" use="user" /> 个元素

<xsl:template match="user[key('posts', @id)/status!='ACTIVE']" />

然后,忽略用户的模板就是这个....

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

    <xsl:key name="posts" match="post" use="user" />

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

    <xsl:template match="post[status!='ACTIVE']" />

    <xsl:template match="status" />

    <xsl:template match="user[key('posts', @id)/status!='ACTIVE']" />
</xsl:stylesheet>

把这一切放在一起给你这个......

{{1}}