首先在这里发帖,找不到我需要的东西。 我有2个XML文档,它们具有相似但略有不同的结构,并且标签中可能有不同的值。 我需要遍历一个XML中的所有叶节点,如果相同的标记(仅通过完整的XPath位置)位于另一个XML中,我需要将值复制到目标XML。 我需要自动执行此操作。不使用属性。
例如,我的基地:
<root>
<a>abc</a>
<b>def</b>
</root>
目标:
<root>
<a>xyz</a>
<c>ghi</c>
</root>
预期产出:
<root>
<a>abc</a>
<c>ghi</c>
</root>
使用XSLT和简单的Linux工具/ shell脚本比第三方工具更受欢迎。 谢谢!
答案 0 :(得分:0)
此 XSLT 3.0 样式表为没有任何子元素的元素生成动态XPath字符串,并使用<xsl:evaluate>
从“基本”XML文件中选择匹配元素(命名为'base.xml'和该示例的XML输入在同一目录中。
如果“base”中有匹配的元素,它会使用该元素的文本,否则会复制匹配元素的文本。
<?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:my="my"
exclude-result-prefixes="xs"
version="3.0">
<xsl:output indent="yes"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<!--Elements that do not have any child elements -->
<xsl:template match="*[not(*)]">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<!--select the equivalent element from the "base" XML file (if any),
using the XPath generated from the my:baseMatchXPath() function -->
<xsl:variable name="baseMatch" as="item()*">
<xsl:evaluate xpath="my:baseMatchXPath(.)" />
</xsl:variable>
<!--
Create a sequence of nodes using the $baseMatch and the currently matched element.
Use a predicate filter to select the first one
(if there is no baseMatch, then the context node is the first).
Apply templates to that element's child node()'s.
NOTE: In this example it is the text() node,
but this would also preserve any comments or processing instructions
-->
<xsl:apply-templates select="($baseMatch, .)[1]/node()"/>
</xsl:copy>
</xsl:template>
<xsl:function name="my:baseMatchXPath" as="xs:string">
<xsl:param name="contextNode"/>
<xsl:variable name="base">document('base.xml')</xsl:variable>
<xsl:sequence select="concat($base,
string-join(my:ancestors($contextNode), ''))"/>
</xsl:function>
<xsl:function name="my:ancestors" as="item()*">
<xsl:param name="contextNode"/>
<xsl:if test="$contextNode/ancestor::*[1]">
<xsl:sequence select="my:ancestors($contextNode/ancestor::*[1])"/>
</xsl:if>
<xsl:sequence select="'/'"/>
<xsl:sequence select="name($contextNode)"/>
<xsl:if test="count($contextNode/parent::*/*[name()=name($contextNode)])
> 1">
<xsl:sequence
select="concat(
'[',
count($contextNode/preceding-sibling::*[name()=name($contextNode)])+1,
']')"/>
</xsl:if>
</xsl:function>
</xsl:stylesheet>
答案 1 :(得分:0)
这是一个XSLT 1.0解决方案 - 我们只假设这两个文档没有混合内容元素(没有元素同时包含元素和非空白文本节点子节点):
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="my:my">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<my:patternDoc>
<root>
<a>abc</a>
<b>def</b>
</root>
</my:patternDoc>
<xsl:variable name="vinitTarget" select="/"/>
<xsl:variable name="vinitPattern" select="document('')/*/my:patternDoc"/>
<xsl:template match="/*">
<xsl:copy>
<xsl:call-template name="clone"/>
</xsl:copy>
</xsl:template>
<xsl:template name="clone">
<xsl:param name="pTargetNode" select="/*/*[1]"/>
<xsl:param name="pPatterntNode" select="$vinitPattern/*/*[1]"/>
<xsl:if test="$pTargetNode">
<xsl:choose>
<xsl:when test="not(name($pTargetNode) = name($pPatterntNode))">
<xsl:copy-of select="$pTargetNode"/>
<xsl:call-template name="clone">
<xsl:with-param name="pTargetNode"
select="$pTargetNode/following-sibling::*[1]"/>
<xsl:with-param name="pPatterntNode"
select="$pPatterntNode/following-sibling::*[1]"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:choose>
<xsl:when test="$pTargetNode[not(*)] and $pPatterntNode[not(*)]">
<xsl:copy-of select="$pPatterntNode"/>
<xsl:call-template name="clone">
<xsl:with-param name="pTargetNode"
select="$pTargetNode/following-sibling::*[1]"/>
<xsl:with-param name="pPatterntNode"
select="$pPatterntNode/following-sibling::*[1]"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="$pTargetNode/*">
<xsl:element name="{name($pTargetNode)}"
namespace="{namespace-uri($pTargetNode)}">
<xsl:call-template name="clone">
<xsl:with-param name="pTargetNode"
select="$pTargetNode/*[1]"/>
<xsl:with-param name="pPatterntNode"
select="$pPatterntNode/*[1]"/>
</xsl:call-template>
</xsl:element>
<xsl:call-template name="clone">
<xsl:with-param name="pTargetNode"
select="$pTargetNode/following-sibling::*[1]"/>
<xsl:with-param name="pPatterntNode"
select="$pPatterntNode/following-sibling::*[1]"/>
</xsl:call-template>
</xsl:when>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
在提供的“目标”XML文档上应用此转换时:
<root>
<a>xyz</a>
<c>ghi</c>
</root>
产生了想要的正确结果:
<root>
<a xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:my="my:my">abc</a>
<c>ghi</c>
</root>
注意强>:
复制的额外命名空间仅仅是因为模式文档当前嵌入到XSLT样式表中。这只是为了方便。在实际情况中,模式文档将与XSLT样式表分开,并且不会复制额外的命名空间节点。
让我们验证提供的转换是否可以在更复杂的XML文档上正常运行:
<root>
<a>
<b>
<c>xyz</c>
<f/>
<g>tuv</g>
</b>
</a>
<d>
<e>ghi</e>
</d>
</root>
如果嵌入式模式文档现在在同一转换中(上图):
<my:patternDoc>
<root>
<a>
<b>
<c>abc</c>
<f/>
<g>mnk</g>
</b>
</a>
<d>
<e>pqr</e>
</d>
</root>
</my:patternDoc>
再次生成想要的正确结果:
<root>
<a>
<b>
<c xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:my="my:my">abc</c>
<f xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:my="my:my"/>
<g xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:my="my:my">mnk</g>
</b>
</a>
<d>
<e xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:my="my:my">pqr</e>
</d>
</root>