[英]merge two or more node in xml using xslt
此問題有3種情況:
第一種可能性:輸入:
<root>
<node id="N1">
<fruit id="1" action="aaa">
<orange id="x" action="create">
<attribute>
<color>Orange</color>
<year>2012</year>
</attribute>
</orange>
<orange id="x" action="change">
<attribute>
<color>Red</color>
</attribute>
</orange>
<orange id="x" action="change">
<attribute>
<color>Blue</color>
<condition>good</condition>
</attribute>
</orange>
</fruit>
</node>
</root>
預期產量:
<root>
<node id="N1">
<fruit id="1" action="aaa">
<orange id="x" action="create">
<attribute>
<color>Blue</color>
<year>2012</year>
<condition>good</condition>
</attribute>
</orange>
</fruit>
</node>
</root>
第二種可能性:輸入:
<root>
<node id="N1">
<car id="1">
<bmw id="i" action="change">
<attribute>
<color>Blue</color>
<owner>a</owner>
</attribute>
</bmw>
<bmw id="i" action="change">
<attribute>
<color>Yellow</color>
<status>avaailable</status>
</attribute>
</bmw>
</car>
</node>
</root>
預期產量:
<root>
<node id="N1">
<car id="1">
<bmw id="i" action="change">
<attribute>
<color>Yellow</color>
<owner>a</owner>
<status>available</status>
</attribute>
</bmw>
</car>
</node>
</root>
第三種情況:
<root>
<node id="N1">
<car id="1">
<bmw id="j" action="delete">
<attribute>
<color>Blue</color>
<year>2000</year>
</attribute>
</bmw>
<bmw id="j" action="delete">
<attribute>
<color>Pink</color>
<status>available</status>
</attribute>
</bmw>
</car>
</node>
</root>
預期產量:
<root>
<node id="N1">
<car id="1">
<bmw id="j" action="delete">
<attribute>
<color>Pink</color>
<year>2000</year>
<status>available</status>
</attribute>
</bmw>
</car>
</node>
</root>
關於第二種情況和第三種情況的說明:
我希望解釋清楚。
請為我提供有關XSLT解決方案的建議。 謝謝。
親切的問候,約翰
與我在這里給您提供的解決方案相比,這是一種不同口味的解決方案。
我認為有必要逐步進行。 我假設@action
的出現順序是合理的-首先create
,然后change
,然后最后remove
。 相同的@action
可以多次出現,但不是隨機的。 現在我們准備看一下主要邏輯:
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
我們聲明身份轉換,然后在幾個地方進行攔截。 我們僅在具有相同@id
,父@id
和@action
的節點的唯一出現處停止:
<xsl:template match="node/*/*[a:is-primary(.)]" priority="1">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:apply-templates select="attribute" mode="consolidate-most-recent"/>
</xsl:copy>
</xsl:template>
我們忽略“重復項”:
<xsl:template match="node/*/*[not(a:is-primary(.))]"/>
並忽略create
后的change
以及所有create
和change
后的remove
。
<xsl:template match="node/*/*[@action = 'change'][a:preceded-by(., 'create')]" priority="2"/>
<xsl:template match="node/*/*[@action = 'create' or action='change'][a:followed-by(., 'remove')]" priority="2"/>
當@action
到唯一的@action
而不是緊跟其后的另一個@action
,我們將做一件簡單的事情-收集具有相同@id
的元素的所有屬性,而忽略@action
並使用它們的“最近”值(在文檔順序中最后出現的那些)。
<xsl:template match="attribute" mode="consolidate-most-recent">
<xsl:copy>
<xsl:for-each-group
select="/root/node/*/*[a:matches(current()/parent::*, ., 'any')]/attribute/*"
group-by="local-name()">
<!-- take the last in the document order -->
<xsl:apply-templates select="current-group()[last()]"/>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
而已。 現在讓我們看一下使其工作的功能:
我們有一把簡化查找的key
<xsl:key name="entity" match="/root/node/*/*" use="concat(parent::*/@id, '_', @id, '_', @action)"/>
一個檢查節點是否是唯一出現的函數(我們可以將其直接添加到模板match
謂詞中,但是由於我們還是從函數開始,讓我們保持不變):
<xsl:function name="a:is-primary" as="xs:boolean">
<xsl:param name="ctx"/>
<!-- need to establish "focus"(context) for the key() function to work -->
<xsl:for-each select="$ctx">
<xsl:sequence select="generate-id($ctx) = generate-id(key('entity', concat($ctx/parent::*/@id, '_', $ctx/@id, '_', $ctx/@action))[1])"/>
</xsl:for-each>
</xsl:function>
一個matches
函數,它將為我們進行各種比較(再次,可以將所有條件放在謂詞中,但是通過這種方式,我們將在真實模板中將其保持整潔):
<xsl:function name="a:matches" as="xs:boolean">
<xsl:param name="src"/>
<xsl:param name="target"/>
<!-- can be one of the following:
'any' - only match the @id(s) and ignore @action
'same' - match by @id(s) and expect $src/@action to match $target/@action
a certain value - match by @id(s) and expect @action to match this value
-->
<xsl:param name="action"/>
<xsl:value-of select="
($src/local-name() = $target/local-name()) and
($src/parent::*/@id = $target/parent::*/@id) and
($src/@id = $target/@id) and
(if ($action = 'any')
then true()
else if ($action = 'same')
then ($target/@action = $src/@action)
else ($target/@action = $action))"/>
</xsl:function>
並且在“原始”頂部的preceded-by
和followed-by
語法糖matches
功能:
<xsl:function name="a:preceded-by" as="xs:boolean">
<xsl:param name="ctx"/>
<xsl:param name="action"/>
<xsl:value-of select="count($ctx/preceding::*[a:matches($ctx, ., $action)]) > 0"/>
</xsl:function>
<xsl:function name="a:followed-by" as="xs:boolean">
<xsl:param name="ctx"/>
<xsl:param name="action"/>
<xsl:value-of select="count($ctx/following::*[a:matches($ctx, ., $action)]) > 0"/>
</xsl:function>
摘要
這是一個完整的轉換:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:a="http://a.com">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="entity" match="/root/node/*/*" use="concat(parent::*/@id, '_', @id, '_', @action)"/>
<xsl:function name="a:is-primary" as="xs:boolean">
<xsl:param name="ctx"/>
<!-- need to establish "focus"(context) for the key() function to work -->
<xsl:for-each select="$ctx">
<xsl:sequence select="generate-id($ctx) = generate-id(key('entity', concat($ctx/parent::*/@id, '_', $ctx/@id, '_', $ctx/@action))[1])"/>
</xsl:for-each>
</xsl:function>
<xsl:function name="a:preceded-by" as="xs:boolean">
<xsl:param name="ctx"/>
<xsl:param name="action"/>
<xsl:value-of select="count($ctx/preceding::*[a:matches($ctx, ., $action)]) > 0"/>
</xsl:function>
<xsl:function name="a:followed-by" as="xs:boolean">
<xsl:param name="ctx"/>
<xsl:param name="action"/>
<xsl:value-of select="count($ctx/following::*[a:matches($ctx, ., $action)]) > 0"/>
</xsl:function>
<xsl:function name="a:matches" as="xs:boolean">
<xsl:param name="src"/>
<xsl:param name="target"/>
<!-- can be one of the following:
'any' - only match the @id(s) and ignore @action
'same' - match by @id(s) and expect $src/@action to match $target/@action
a certain value - match by @id(s) and expect @action to match this value
-->
<xsl:param name="action"/>
<xsl:value-of select="
($src/local-name() = $target/local-name()) and
($src/parent::*/@id = $target/parent::*/@id) and
($src/@id = $target/@id) and
(if ($action = 'any')
then true()
else if ($action = 'same')
then ($target/@action = $src/@action)
else ($target/@action = $action))"/>
</xsl:function>
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="node/*/*[a:is-primary(.)]" priority="1">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:apply-templates select="attribute" mode="consolidate-most-recent"/>
</xsl:copy>
</xsl:template>
<xsl:template match="attribute" mode="consolidate-most-recent">
<xsl:copy>
<xsl:for-each-group
select="/root/node/*/*[a:matches(current()/parent::*, ., 'any')]/attribute/*"
group-by="local-name()">
<!-- take the last in the document order -->
<xsl:apply-templates select="current-group()[last()]"/>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
<xsl:template match="node/*/*[not(a:is-primary(.))]"/>
<!-- assume a remove is never followed by a change or create -->
<xsl:template match="node/*/*[@action = 'change'][a:preceded-by(., 'create')]" priority="2"/>
<xsl:template match="node/*/*[@action = 'create' or action='change'][a:followed-by(., 'remove')]" priority="2"/>
</xsl:stylesheet>
當應用於文檔時:
<root>
<node id="N1">
<fruit id="1" action="aaa">
<orange id="x" action="create">
<attribute>
<color>Orange</color>
<year>2012</year>
</attribute>
</orange>
<orange id="x" action="change">
<attribute>
<color>Red</color>
<something>!!</something>
</attribute>
</orange>
<orange id="x" action="change">
<attribute>
<color>Blue</color>
<condition>good</condition>
</attribute>
</orange>
<orange id="x" action="remove">
<attribute>
<condition>awesome</condition>
</attribute>
</orange>
</fruit>
</node>
</root>
產生以下結果:
<root>
<node id="N1">
<fruit id="1" action="aaa">
<orange id="x" action="remove">
<attribute>
<color>Blue</color>
<year>2012</year>
<something>!!</something>
<condition>awesome</condition>
</attribute>
</orange>
</fruit>
</node>
</root>
我希望這很清楚。 您可以擴展這個概念,並為自己創建一個漂亮的庫,包含這些可重用函數,然后將它們用作簡單謂詞以一種方式或另一種方式來合並節點。 不可能是完成工作的最有效方法,但至少是表達解決方案的干凈方法。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.