我需要将xml文件从一个版本转换为另一个版本,方法是将新元素及其子元素添加到旧版本,以使其与较新版本兼容。 (版本1 - >版本2)其中版本2具有一些新的必需元素。如果缺少元素或缺少子元素,我们需要添加默认值。顺序也很重要。
例如版本1:
<root>
<a>
<a1>A1</a1>
<a2>A2</a2>
</a>
<b>
<b1>B1</b1>
</b>
</root>
第2版:
<root>
<a>
<a1>A1</a1>
<a3>A3</a3>
<a2>A2</a2>
</a>
<c>
<c1>C1</c1>
<c2>C2</c2>
</c>
<d>
<d1>D1</d1>
<d2>D2</d2>
<d3>D3</d3>
</d>
<b>
<b1>B1</b1>
</b>
</root>
我尝试了几件事,但似乎遇到了我无法通过的路障。我为默认值创建变量,希望循环遍历它们并根据需要添加。
<xsl:variable name="defaultA">
<a1>aOne<a1>
<a2>aTwo<a2>
</xsl:variable>
.
.
.
<xsl:variable name="defaultC">
<c1>cOne<c1>
<c2>cTwo<c2>
</xsl:variable>
<xsl:variable name="defaultB">
<b1>bOne<b1>
<b2>bTwo<b2>
</xsl:variable>
<xsl:template match="root">
<xsl:variable name="defaults" select="document('')/*/xsl:variable[contains(@name,'default')]/*" />
<xsl:variable name="defaultNodes" select="ext:node-set($defaults)"/>
<xsl:copy>
<xsl:for-each select="$defaultNodes">
<xsl:copy-of select="node()[not(name() = name(current()))]"/>
</xsl:for-each>
<xsl:apply-templates select="node()|@*" />
</xsl:copy>
</xsl:template>
这将添加节点,但它将命名空间添加到元素(我不想要),并且如果它们已经存在,将继续添加元素事件。
任何有助于我朝着正确方向前进的帮助都将不胜感激。提前谢谢。
答案 0 :(得分:1)
最方便的方法在很大程度上取决于您的内容模型的外观,以及v2中是否存在任何元素,但在v1中是否可选。
根据你的说法,我猜是
如果这些猜测错了,您需要在答案中做出适当调整(您可能会考虑尝试让问题更清晰)。
有几个案例需要考虑:
作为示例,假设v1中所需的元素具有以“R”开头的名称,并且类似于v1中可选且不存在的元素具有以“O”和“A”开头的名称。 v1中所需的元素和可选元素的名称以“R”和“O”结尾。所以我们有六个案例,这里用元素AO,AR,OO,OR,RO,RR表示。 v1的内容模型为(OO?, OR?, RO, RR)
,v2的内容模型为(AO?, AR, OO?, OR, RO?, RR)
。
处理父元素的简单模板(我称之为P)看起来像这样(未经测试):
<xsl:template match="P">
<!--* AO is optional and will never appear in v1 input.
* So we do nothing. *-->
<!--* AR is required and will never appear in v1 input.
* So we inject it unconditionally. *-->
<xsl:call-template name="default-AR"/>
<!--* OO is optional and might appear in v1 input.
* So we keep it if it's here. *-->
<xsl:apply-templates select="OO"/>
<!--* OR is required and might appear in v1 input.
* We keep it if it's already present and supply a
* default version if it's missing. *-->
<xsl:apply-templates select="OR"/>
<xsl:if test="not(OR)">
<xsl:call-template name="default-OR"/>
</xsl:if>
<!--* RO is optional but will always appear in v1 input.
* We keep it. *-->
<xsl:apply-templates select="RO"/>
<!--* RR is required in v1 and v2. Just keep it. *-->
<xsl:apply-templates select="RR"/>
</xsl:template>
如果您在六个月后阅读此样式表,您会发现元素OR的逻辑更容易使用xsl:choose,使用它而不是上面显示的代码。如果您发现apply-templates后跟if更容易理解,请使用它。
<xsl:choose>
<xsl:when test="OR">
<xsl:apply-templates select="OR"/>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="default-OR"/>
</xsl:otherwise>
</xsl:choose>
如果内容模型不是平面序列,模板的逻辑可能会变得更加复杂;在一般情况下,不清楚从内容模型中具有更多元素类型和可能不同结构的从v1到v2的转换是可能的。 (你的假设是我猜测v1和v2内容模型都是扁平序列的主要原因。)