[英]XSLT to Merge 2 XML Files
我知道這里很少有xml / xslt合並相關的問題,但是似乎沒有一個可以解決我的問題。
我正在尋找的是XSLT(盡可能通用-與輸入XML文件的結構不緊密),它可以
將a.xml與b.xml合並並生成c.xml
例如:合並a.xml :
<root_node>
<settings>
<setting1>a1</setting1>
<setting2>a2</setting2>
<setting3>
<setting31>a3</setting31>
</setting3>
<setting4>a4</setting4>
</settings>
</root_node>
與b.xml :
<root_node>
<settings>
<setting1>b1</setting1>
<setting2>b2</setting2>
<setting3>
<setting31>b3</setting31>
</setting3>
<setting5 id="77">b5</setting5>
</settings>
</root_node>
將生成c.xml :
<root_node>
<settings>
<setting1>a1</setting1>
<setting2>a2</setting2>
<setting3>
<setting31>a3</setting31>
</setting3>
<setting5 id="77">b5</setting5>
</settings>
附加信息
我將嘗試通過“公共節點”來解釋我的理解。 這可能不是准確的xml / xslt定義,因為我不是任何專家。
a / root_node / settings / setting1是b / root_node / settings / setting1的“公共節點”,因為使用相同路徑訪問了2個節點。 設置2和設置3相同。
在2“非公共節點”是一個 / root_node /設置/ setting4其僅在A.XML實測值(它應該在輸出不來)和b / root_node /設置/ setting5其僅在B.XML實測值(它應該進入輸出)。
我所說的“通用解決方案”並不是說某種東西可以用輸入XML的任何格式工作。 我的意思是,xslt不應包含硬編碼的xpath,而您可能會添加諸如“僅當a.xml中的節點是唯一的時這才有效”之類的限制,或者您可能認為合適的任何其他限制。
對多個文件進行操作的基本技術是通過document()函數。 文檔功能如下所示:
<xsl:variable name="var1" select="document('http://example.com/file1.xml', /)"/>
<xsl:variable name="var2" select="document('http://example.com/file2.xml', /)"/>
擁有兩個文檔后,就可以像在同一文檔中一樣使用它們的內容。
以下XSLT 1.0程序可以滿足您的需求。
將其應用於b.xml
,並將路徑作為參數傳遞給a.xml
。
下面是它的工作原理。
B
,因為它包含要保留的新節點以及A
和B
之間的公共元素 。
ancestor-or-self
軸)的斜杠分隔列表。 B
, <setting31>
將具有簡單的root_node/settings/setting3/setting31/
路徑 。 calculatePath
的模板calculatePath
。 nodeValueByPath
,嘗試從另一個文檔中檢索相應簡單路徑的文本值。 B
的值。 這滿足您的第二個要點。 結果,新文檔匹配B
的結構並包含:
B
中所有在A
沒有對應節點的文本節點值。 B
的對應節點存在時,來自A
文本節點值。 這是XSLT:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" />
<xsl:param name="aXmlPath" select="''" />
<xsl:param name="aDoc" select="document($aXmlPath)" />
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()" />
</xsl:copy>
</xsl:template>
<!-- text nodes will be checked against doc A -->
<xsl:template match="*[not(*)]/text()">
<xsl:variable name="path">
<xsl:call-template name="calculatePath" />
</xsl:variable>
<xsl:variable name="valueFromA">
<xsl:call-template name="nodeValueByPath">
<xsl:with-param name="path" select="$path" />
<xsl:with-param name="context" select="$aDoc" />
</xsl:call-template>
</xsl:variable>
<xsl:choose>
<!-- either there is something at that path in doc A -->
<xsl:when test="starts-with($valueFromA, 'found:')">
<!-- remove prefix added in nodeValueByPath, see there -->
<xsl:value-of select="substring-after($valueFromA, 'found:')" />
</xsl:when>
<!-- or we take the value from doc B -->
<xsl:otherwise>
<xsl:value-of select="." />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- this calcluates a simpe path for a node -->
<xsl:template name="calculatePath">
<xsl:for-each select="..">
<xsl:call-template name="calculatePath" />
</xsl:for-each>
<xsl:if test="self::*">
<xsl:value-of select="concat(name(), '/')" />
</xsl:if>
</xsl:template>
<!-- this retrieves a node value by its simple path -->
<xsl:template name="nodeValueByPath">
<xsl:param name="path" select="''" />
<xsl:param name="context" select="''" />
<xsl:if test="contains($path, '/') and count($context)">
<xsl:variable name="elemName" select="substring-before($path, '/')" />
<xsl:variable name="nextPath" select="substring-after($path, '/')" />
<xsl:variable name="currContext" select="$context/*[name() = $elemName][1]" />
<xsl:if test="$currContext">
<xsl:choose>
<xsl:when test="contains($nextPath, '/')">
<xsl:call-template name="nodeValueByPath">
<xsl:with-param name="path" select="$nextPath" />
<xsl:with-param name="context" select="$currContext" />
</xsl:call-template>
</xsl:when>
<xsl:when test="not($currContext/*)">
<!-- always add a prefix so we can detect
the case "exists in A, but is empty" -->
<xsl:value-of select="concat('found:', $currContext/text())" />
</xsl:when>
</xsl:choose>
</xsl:if>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.