简体   繁体   English

XSLT合并两个XML结构

[英]XSLT Merge two XML structures

I have two xml structures in two variables which I need to merge. 我在两个需要合并的变量中有两个xml结构。 I tried writing an XSLT stylesheet based on different awnsers on stackoverflow, but I was not successful. 我尝试在stackoverflow上基于不同的遮篷编写XSLT样式表,但未成功。

The structure of the first one looks like this: 第一个的结构如下所示:

<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>

And the second one looks like that: 第二个看起来像这样:

<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>

As you can see in the second one there are some missing informations. 如您在第二篇中看到的,有一些缺失的信息。 In the shortDescription there is missing the text for textHead and textBody and in longDescription there is mussing the text for textBody. 在shortDescription中缺少textHead和textBody的文本,在longDescription中缺少textBody的文本。 There could be missing no text, some text or all text. 可能没有任何文本,某些文本或所有文本。 Now I want to take the missing informations out of the first xml structure and copy them into the second one and mark the changes with a div tag. 现在,我想从第一个xml结构中删除丢失的信息,并将它们复制到第二个xml结构中,并使用div标签标记更改。

The output should look like that: 输出应如下所示:

    <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>

I can use XSLT 2.0 for that task. 我可以将XSLT 2.0用于该任务。 Is it possible to do something like this with XSLT? 可以使用XSLT做类似的事情吗?

Here is an example how you could solve it using XSLT 3.0 (as supported by the latest versions of Saxon 9 and Altova) and exploiting xsl:evaluate ( https://www.w3.org/TR/xslt-30/#dynamic-xpath ) and the path function ( https://www.w3.org/TR/xpath-functions-31/#func-path ): 这是一个示例,您可以使用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>

Note that while Saxon 9.8 HE supports XSLT 3.0 the xsl:evaluate element is unfortunately only supported in the commercial editions. 请注意,虽然Saxon 9.8 HE支持XSLT 3.0,但是不幸的是,仅在商业版本中支持xsl:evaluate元素。

If the set of elements you want to merge is limited, it might be clearer to match every element like that explicitly and then just copy over the content from the other file, but if you want a more generic way to achieve something like this, here's one option: 如果要合并的元素集受到限制,则可以更明确地匹配每个类似的元素,然后仅复制另一个文件中的内容,但是如果您希望采用更通用的方式来实现类似目的,则可以一种选择:

<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>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM