我有一个具有以下结构的XML文件(多个"实体"节点):
<!-- entities.xml -->
<root>
<entity template="foo-template" kind="foo" name="bar">
<groups>
<group id="1">
<definition id="1" name="foobar" />
</group>
</groups>
</entity>
</root>
许多entity
个节点具有相似的属性和子节点。我想允许用户在单独的文件中创建entity
个模板。引用模板将按如下方式完成:
<entity template="foo-template" kind="foo" ... />
来自&#34; foo-template&#34;的每个属性和子节点应该被复制到entity
,除了已经存在的那些(即允许覆盖模板)。
我对XSLT不太熟悉。它是否适合执行此任务,或者我最好不执行此任务?
我使用C ++和RapidXml,但可以使用其他XML库。
编辑:示例。
模板文件:
<!-- templates.xml -->
<templates>
<entity template="foo-template" name="n/a" model="baz">
<groups>
<group id="1">
<definition id="1" name="def1" />
<definition id="2" name="def2" />
</group>
<group id="2">
<definition id="1" name="def3" />
<definition id="2" name="def4" />
</group>
</groups>
</entity>
</templates>
输出文件:
<!-- output.xml -->
<root>
<entity kind="foo" name="bar" model="baz">
<groups>
<group id="1">
<definition id="1" name="foobar" />
</group>
<group id="2">
<definition id="1" name="def3" />
<definition id="2" name="def4" />
</group>
</groups>
</entity>
</root>
因此输出包含来自&#34; entities.xml&#34;的组1。来自&#34; templates.xml&#34;的第2组和第2组。无需合并具有相同ID的group
个节点。
答案 0 :(得分:2)
如果您的文件templates.xml
看起来像
<templates>
<entity template="foo-template" kind="foo" name="bar" model="baz" />
<!-- and other entity elements with different template="..." values -->
</templates>
然后,如下所示的XSLT将实现您的目标
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:key name="kEntityTemplate" match="entity" use="@template" />
<!-- identity template - copy everything not overridden by another template -->
<xsl:template match="@*|node">
<xsl:copy><xsl:apply-templates select="@*|node()"/></xsl:copy>
</xsl:template>
<xsl:template match="entity[@template]">
<xsl:variable name="thisEntity" select="." />
<!-- switch to templates doc -->
<xsl:for-each select="document('templates.xml')">
<xsl:variable name="template"
select="key('kEntityTemplate', $thisEntity/@template)" />
<entity>
<!-- copy template attributes that are not overridden -->
<xsl:for-each select="$template/@*">
<xsl:if test="not($thisEntity/@*[name() = name(current())])">
<!-- if not, copy the one from the template -->
<xsl:apply-templates select="." />
</xsl:if>
</xsl:for-each>
<!-- copy source attributes -->
<xsl:apply-templates select="$thisEntity/@*[name() != 'template']" />
<!-- deal with elements -->
<xsl:if test="$thisEntity/groups/group | $template/groups/group">
<groups>
<!-- here we select all group elements from the source plus
those group elements from the template that do not also exist
in the source, and sort the whole lot by id -->
<xsl:apply-templates select="$thisEntity/groups/group
| $template/groups/group[not(@id = $thisEntity/groups/group/@id)]">
<xsl:sort select="@id" data-type="number" />
</xsl:apply-templates>
</groups>
</xsl:if>
</entity>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
templates.xml
文件需要与样式表位于同一目录中。
答案 1 :(得分:0)
您在执行任何类型的XML转换之外的一个选项是导入其他XML文件,然后从标记内引用它。有关示例,请参阅here。
这将要求您的用户为您可能不需要的每种模板类型分别提供模板文件。但是我更喜欢导入方法,因为kiss principle。如果您不熟悉XSLT,那么导入也可能是更好的方法。
我希望这有帮助!