XSLT合并两个XML结构

时间:2017-06-21 04:30:28

标签: xml xslt merge xslt-2.0

我需要合并两个变量中的两个xml结构。我尝试在stackoverflow上基于不同的awnsers编写一个XSLT样式表,但是我没有成功。

第一个的结构如下:

<root>
    <content>
        <text-block>
            <descriptionHead>
                Some description text for the text block head.
            </descriptionHead>
            <description>
                Some description text block text.
            </description>
        </text-block>
        <shortDescription>
            <textHead>
                Example text for the short description head.
            </textHead>
            <textBody>
                Example text for the short description text body.
            </textBody>
        </shortDescription>
        <longDescription>
            <textHead>
                Example text for the long description head.
            </textHead>
            <textBody>
                Example text for the short description text body.
            </textBody>
        </longDescription>
    </content>
</root>

第二个看起来像那样:

<root>
    <content>
        <text-block>
            <descriptionHead>
                Some text 1.
            </descriptionHead>
            <description>
                Some text 2.
            </description>
        </text-block>
        <shortDescription>
            <textHead></textHead>
            <textBody></textBody>
        </shortDescription>
        <longDescription>
            <textHead>
                Some text 3.
            </textHead>
            <textBody></textBody>
        </longDescription>
    </content>
</root>

正如您在第二个中看到的那样,有一些缺失的信息。 在shortDescription中缺少textHead和textBody的文本,在longDescription中有textbody的文本。可能缺少任何文本,一些文本或所有文本。 现在我想从第一个xml结构中取出缺失的信息,然后将它们复制到第二个xml结构中,并使用div标记标记更改。

输出应该如下:

    <root>
    <content>
        <text-block>
            <descriptionHead>
                Some text 1.
            </descriptionHead>
            <description>
                Some text 2.
            </description>
        </text-block>
        <shortDescription>
            <textHead>
                <div class="merged">
                    Example text for the short description head.
                </div>
            </textHead>
            <textBody>
                <div class="merged">
                    Example text for the short description text body.
                </div>
            </textBody>
        </shortDescription>
        <longDescription>
            <textHead>
                Some text 3.
            </textHead>
            <textBody>
                <div class="merged">
                    Example text for the short description text body.
                </div>
            </textBody>
        </longDescription>
    </content>
</root>

我可以使用XSLT 2.0完成该任务。是否可以使用XSLT做这样的事情?

2 个答案:

答案 0 :(得分:2)

以下是使用XSLT 3.0(由最新版本的Saxon 9和Altova支持)并利用xsl:evaluatehttps://www.w3.org/TR/xslt-30/#dynamic-xpath)和path函数解决问题的示例(https://www.w3.org/TR/xpath-functions-31/#func-path):

<?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:math="http://www.w3.org/2005/xpath-functions/math"
    exclude-result-prefixes="xs math"
    version="3.0">

    <xsl:param name="doc2-uri" as="xs:string" select="'name-of-first-input-in-questions.xml'"/>
    <xsl:param name="doc2" select="doc($doc2-uri)"/>

    <xsl:mode on-no-match="shallow-copy"/>

    <xsl:template match="*[not(has-children())]">
        <xsl:copy>
            <div class="merged">
                <xsl:evaluate context-item="$doc2" xpath="path() || '/text()'"></xsl:evaluate>
            </div>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

请注意,虽然Saxon 9.8 HE支持XSLT 3.0,但遗憾的是,xsl:evaluate元素仅在商业版中受支持。

答案 1 :(得分:0)

如果要合并的元素集是有限的,那么明确匹配每个元素可能更清楚,然后只是复制其他文件中的内容,但是如果你想要更通用的方法来实现类似的东西这个,这是一个选项:

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

  <xsl:output method="xml" indent="yes"/>

  <!-- Parse the other XML file and store it in memory. -->
  <xsl:param name="OTHER" select="doc('input-1.xml')"/>

  <!--
  Given a node in an XML document, get the names of all its ancestor elements
  and the name of the element itself as a sequence of strings.

  For example, for root/content/text-block/descriptionHead, this returns:

    ('root', 'content', 'text-block', 'descriptionHead')
  -->
  <xsl:function name="local:lineage" as="xs:string*">
    <xsl:param name="ctx" as="node()"/>

    <xsl:sequence select="
      for $a in $ctx/ancestor-or-self::* return xs:string(node-name($a))
    "/>
  </xsl:function>

  <!-- Match children of content/* that don't have any text content. -->
  <xsl:template match="content/*/*[not(normalize-space(.))]">
    <xsl:variable name="lineage" select="local:lineage(.)"/>

    <xsl:copy>
      <div class="merged">
        <!--
        In the other XML document, find the element with the same "lineage" as
        the current element and apply the template in this stylesheet that
        match the text node children of that element.

        For example, for root/content/text-block/descriptionHead, this
        apply-templates call applies the template that matches the text inside
        root/content/text-block/descriptionHead in the other XML file.

        In this stylesheet, the matching template is the identity template
        below, which copies elements into the output as is.
        -->
        <xsl:apply-templates select="
          $OTHER/root/content/*/*[deep-equal(local:lineage(.), $lineage)]/text()
        "/>
      </div>
    </xsl:copy>
  </xsl:template>

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

</xsl:stylesheet>