我需要确保XML文档始终包含这些节点:
<group>
<section>0001</section>
<head>0002</head>
<body>0003</body>
</group>
典型的XML输入文件如下所示(组节点应始终位于类别和摘要之前):
<story>
<group>
<section>section-content</section>
<head>head-content</head>
<body>body-content</body>
<extra>extra-content</extra>
</group>
<category>some text</category>
<summary>some text</summary>
</story>
但无法保证组元素或其任何子元素都存在。
所以给出了一个XML文档:
<story>
<category>some text</category>
<summary>some text</summary>
</story>
输出应如下所示:
<story>
<group>
<section>0001</section>
<head>0002</head>
<body>0003</body>
</group>
<category>some text</category>
<summary>some text</summary>
</story>
XSLT不应修改现有内容,例如
<story>
<group>
<section>existing text</section>
<extra>existing text</extra>
</group>
<category>some text</category>
<summary>some text</summary>
</story>
应转换为:
<story>
<group>
<section>existing text</section>
<head>0002</head>
<body>0003</body>
<extra>existing text</extra>
</group>
<category>some text</category>
<summary>some text</summary>
</story>
以Tomalak's答案为依据,我提出了这个问题:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:subst="http://tempuri.com/mysubst"
>
<xsl:strip-space elements="*"/>
<xsl:output method="xml" encoding="utf-8" indent="yes"/>
<!-- These defaults (elements and contents) can be modified at any time -->
<subst:defaults>
<subst:element name="group">
<subst:section>0001</subst:section>
<subst:head>0002</subst:head>
<subst:body>0002</subst:body>
</subst:element>
</subst:defaults>
<!-- this makes the above available as a variable -->
<xsl:variable name="defaults" select="document('')/*/subst:defaults" />
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="story[not(group)]">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:element name="group"></xsl:element>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="group">
<xsl:copy>
<xsl:copy-of select="@*|*"/>
<xsl:call-template name="create-defaults" />
</xsl:copy>
</xsl:template>
<!-- Insert the defaults-->
<xsl:template name="create-defaults">
<xsl:variable name="this" select="." />
<xsl:for-each select="$defaults/subst:element[@name = name($this)]/*">
<xsl:if test="not($this/*[name() = local-name(current())])">
<xsl:apply-templates select="." />
</xsl:if>
</xsl:for-each>
</xsl:template>
<!-- Remove the namespaces -->
<xsl:template match="*">
<xsl:element name="{local-name()}">
<xsl:apply-templates select="@*|node()"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
如果group元素已经存在似乎有效,但如果group元素不存在,我无法弄清楚如何让它工作。
答案 0 :(得分:1)
此XSLT 1.0转换
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:subst="http://tempuri.com/mysubst"
exclude-result-prefixes="subst"
>
<xsl:strip-space elements="*"/>
<xsl:output method="xml" encoding="utf-8" indent="yes"/>
<subst:defaults>
<subst:element name="group">
<subst:element name="section">0001</subst:element>
<subst:element name="head">0002</subst:element>
<subst:element name="body">0003</subst:element>
</subst:element>
</subst:defaults>
<xsl:variable name="subst" select="document('')/*/subst:defaults/subst:element" />
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<!-- subst:element outputs a new element with the given @name -->
<xsl:template match="subst:element">
<xsl:element name="{@name}">
<!-- this would also copy any additional "default" attribute! -->
<xsl:apply-templates select="@*[not(name() = 'name')] | node()"/>
</xsl:element>
</xsl:template>
<!-- only stories without any group get "special" treatment -->
<xsl:template match="story[not(group)]">
<xsl:copy>
<xsl:apply-templates select="@*" />
<xsl:apply-templates select="$subst[@name = 'group']" mode="copy-or-default" />
<xsl:apply-templates />
</xsl:copy>
</xsl:template>
<!-- a group first outputs all "default" children, then any extra children -->
<xsl:template match="group">
<xsl:variable name="defaults" select="$subst[@name = 'group']/*" />
<xsl:copy>
<xsl:apply-templates select="@*" />
<xsl:apply-templates select="$defaults" mode="copy-or-default">
<xsl:with-param name="parent" select="." />
</xsl:apply-templates>
<!-- any elements that don't have a default AND any non-element children -->
<xsl:apply-templates select="
*[not(name() = $defaults/@name)] | node()[not(self::*)]
" />
</xsl:copy>
</xsl:template>
<!-- in "copy-or-default" mode, this checks the context parent
and either copies the element from there or uses the default -->
<xsl:template match="subst:element" mode="copy-or-default">
<xsl:param name="parent" />
<xsl:choose>
<xsl:when test="$parent and $parent/*[name() = current()/@name]">
<xsl:apply-templates select="$parent/*[name() = current()/@name]" />
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="." />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
应用于此输入
<x>
<story>
<group>
<section>0001</section>
<head>0002</head>
<body>0003</body>
</group>
<category>some text</category>
<summary>some text</summary>
</story>
<story>
<group>
<body>0004</body>
<!-- a comment! -->
<foo>blah</foo>
</group>
<category>some text</category>
<summary>some text</summary>
</story>
<story>
<category>some text</category>
<summary>some text</summary>
</story>
</x>
产生
<x>
<story>
<group>
<section>0001</section>
<head>0002</head>
<body>0003</body>
</group>
<category>some text</category>
<summary>some text</summary>
</story>
<story>
<group>
<section>0001</section>
<head>0002</head>
<body>0004</body>
<!-- a comment! -->
<foo>blah</foo>
</group>
<category>some text</category>
<summary>some text</summary>
</story>
<story>
<group>
<section>0001</section>
<head>0002</head>
<body>0003</body>
</group>
<category>some text</category>
<summary>some text</summary>
</story>
</x>