我有一份来源文件:
<?xml version="1.0"?>
<source>
<ItemNotSubstituted/>
<ItemToBeSubstituted Id='MatchId' />
</source>
包含我想要替换为源的内容的样式表:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes" method="xml" omit-xml-declaration="no" version="1.0"/>
<xsl:preserve-space elements="//*"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="ItemToBeSubstituted[@Id = 'MatchId']">
<xsl:copy>
<xsl:copy-of select="@*|*"/>
<Element1/>
<Element2 Value="foo"/>
<Element3 Value="bar"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
此样式表成功将<Element1/><Element2 Value="foo"/><Element3 Value="bar"/>
复制到ItemToBeSubstituted
。但是当我使用不同的源文档时,其中ItemToBeSubstituted
已经有内容:
<?xml version="1.0"?>
<source>
<ItemNotSubstituted/>
<ItemToBeSubstituted Id='MatchId'>
<Element3 Value="baz"/>
</ItemToBeSubstituted>
</source>
我得到了这个输出:
<?xml version="1.0"?>
<source>
<ItemNotSubstituted/>
<ItemToBeSubstituted Id="MatchId">
<Element3 Value="baz"/>
<Element1/>
<Element2 Value="foo"/>
<Element3 Value="bar"/>
</ItemToBeSubstituted>
</source>
我想只替换样式表中源文档中尚不存在的元素。在将样式表应用于第二个文档后,这是我正在寻找的输出,只有源文档中的<Element3>
元素:
<?xml version="1.0"?>
<source>
<ItemNotSubstituted/>
<ItemToBeSubstituted Id="MatchId">
<Element3 Value="baz"/>
<Element1/>
<Element2 Value="foo"/>
</ItemToBeSubstituted>
</source>
使用XSL执行此操作的最佳方法是什么?样式表可能包含许多要替换的元素。所以我不想使用每个元素周围需要<xsl:if>
的方法。有没有比使用一个样式表插入内容更好的方法,然后有一个删除重复元素的第二个样式表?
答案 0 :(得分:2)
我会使用类似的东西:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes" method="xml" omit-xml-declaration="no" version="1.0"/>
<xsl:preserve-space elements="//*"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="ItemToBeSubstituted[@Id = 'MatchId']">
<xsl:variable name="node" select="." />
<xsl:copy>
<xsl:copy-of select="@*|*"/>
<xsl:for-each select="document('elements.xml')/elements/*">
<xsl:if test="not($node/*[name() = name(current())])">
<xsl:copy-of select="." />
</xsl:if>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
其中elements.xml是存储默认添加的元素的文件
<?xml version="1.0" encoding="utf-8" ?>
<elements>
<Element1/>
<Element2 Value="foo"/>
<Element3 Value="bar"/>
</elements>
使用<for-each>
我们迭代默认元素,检查该名称是否有一个元素作为当前节点的子元素,如果没有,则添加它。
答案 1 :(得分:2)
此XSLT 1.0解决方案可以满足您的需求:
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:subst="http://tempuri.org/mysubst"
>
<!-- expand this section to contain all your default elements/values -->
<subst:defaults>
<subst:element name="ItemToBeSubstituted" id="MatchId">
<subst:Element1/>
<subst:Element2 Value="foo"/>
<subst:Element3 Value="bar"/>
</subst:element>
</subst:defaults>
<!-- this makes the above available as a variable -->
<xsl:variable name="defaults" select="document('')/*/subst:defaults" />
<!-- identity template -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<!-- expand the match expression to contain all elements
names that need default values -->
<xsl:template match="ItemToBeSubstituted">
<xsl:copy>
<xsl:copy-of select="@*|*"/>
<xsl:call-template name="create-defaults" />
</xsl:copy>
</xsl:template>
<!-- this does all the heavy lifting -->
<xsl:template name="create-defaults">
<xsl:variable name="this" select="." />
<xsl:for-each select="
$defaults/subst:element[@name = name($this) and @id = $this/@Id]/*
">
<xsl:if test="not($this/*[name() = local-name(current())])">
<xsl:apply-templates select="." />
</xsl:if>
</xsl:for-each>
</xsl:template>
<!-- create the default nodes without namespaces -->
<xsl:template match="subst:*">
<xsl:element name="{local-name()}">
<xsl:apply-templates select="subst:*|@*" />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
使用单独的命名空间(“subst”)可以使样式表中的默认值保持不变。这是否是一件好事取决于,至少你不必有四个文件。
如果您希望将样式表与默认值分离,请将它们放在一个额外的文件中,然后使用此行。
<xsl:variable name="defaults" select="document('defaults.xml')/subst:defaults" />
一旦你这样做,你就可以放弃所有额外的命名空间处理,最终会得到Josh Davis建议的解决方案,或多或少。