【发布时间】:2016-04-21 20:28:09
【问题描述】:
我需要在 Visual Studio 2013 中使用 XLST 1.0 转换一些 XML。
我有以下 XML:
<?xml version="1.0" encoding="utf-8"?>
<root>
<MessageTemplates>
<MessageTemplate>
<Segment name="Uno" cardinality="first">
<value>something</value>
</Segment>
<Segment name="Dos" cardinality="second">
<value>something</value>
</Segment>
<Segment name="Tres" cardinality="third">
<value>something</value>
</Segment>
<Segment name="Quatro" cardinality="third">
<value>something</value>
</Segment>
<Segment name="Cinco" cardinality="second">
<value>something</value>
</Segment>
<Segment name="Seis" cardinality="third">
<value>something</value>
</Segment>
<Segment name="Siete" cardinality="first">
<value>something</value>
</Segment>
</MessageTemplate>
</MessageTemplates>
</root>
Segment 节点的cardinality 属性是有序的,first 是最高的,third 是最低的。我需要创建嵌套级别,基于cardinality,如下:
<?xml version="1.0" encoding="utf-8"?>
<root>
<MessageTemplates>
<MessageTemplate>
<Cardinality type="first">
<Segment name="Uno">
<value>something</value>
</Segment>
<Cardinality type="second">
<Segment name="Dos">
<value>something</value>
</Segment>
<Cardinality type="third">
<Segment name="Tres">
<value>something</value>
</Segment>
<Segment name="Quatro">
<value>something</value>
</Segment>
</Cardinality>
<Segment name="Cinco">
<value>something</value>
</Segment>
<Cardinality type="third">
<Segment name="Seis">
<value>something</value>
</Segment>
</Cardinality>
</Cardinality>
<Segment name="Siete">
<value>something</value>
</Segment>
</Cardinality>
</MessageTemplate>
</MessageTemplates>
</root>
我尝试了几种不同的方法来转换这个文件,但都失败了。我搜索了 SO 并阅读了数十篇文章,但没有找到任何符合我想要做的事情的案例。我还尝试寻找增量方法来实现我的目标,例如使用递归模板调用一次只处理一个 Segment 等。我最接近的是使用以下 XSLT:
<xsl:template match="MessageTemplates/MessageTemplate">
<MessageTemplate>
<xsl:copy-of select="@*"/>
<xsl:call-template name="cardinality"/>
</MessageTemplate>
</xsl:template>
<xsl:template name="cardinality" match="MessageTemplates/MessageTemplate/Segment">
<xsl:choose>
<xsl:when test="position() = 1">
<Cardinality type="{Segment/@cardinality}">
<Segment>
<xsl:apply-templates select="@*[name() != 'cardinality'] | node()" />
</Segment>
</Cardinality>
</xsl:when>
<xsl:when test="position() != last() and following-sibling::Segment/@cardinality != @cardinality">
<Cardinality type="{@cardinality}">
<Segment>
<xsl:apply-templates select="@*[name() != 'cardinality'] | node()" />
</Segment>
</Cardinality>
</xsl:when>
<xsl:when test="position() = last()">
<Segment>
<xsl:apply-templates select="@*[name() != 'cardinality'] | node()" />
</Segment>
</xsl:when>
</xsl:choose>
</xsl:template>
生成了以下 XML:
<?xml version="1.0" encoding="utf-8"?>
<root>
<Version>1.0</Version>
<MessageTemplates>
<MessageTemplate>
<Cardinality type="first">
<Segment>
<Cardinality type="">
<Segment name="Uno">
<value>something</value>
</Segment>
</Cardinality>
<Cardinality type="second">
<Segment name="Dos">
<value>something</value>
</Segment>
</Cardinality>
<Cardinality type="third">
<Segment name="Tres">
<value>something</value>
</Segment>
</Cardinality>
<Cardinality type="third">
<Segment name="Quatro">
<value>something</value>
</Segment>
</Cardinality>
<Cardinality type="second">
<Segment name="Cinco">
<value>something</value>
</Segment>
</Cardinality>
<Cardinality type="third">
<Segment name="Seis">
<value>something</value>
</Segment>
</Cardinality>
<Segment name="Siete">
<value>something</value>
</Segment>
</Segment>
</Cardinality>
</MessageTemplate>
</MessageTemplates>
</root>
基本上,我想要的是将 all Segment 节点包装在单个 Cardinality 节点中。然后,如果下一个Segment 的cardinality 值低于当前Segment 的cardinality 值,我想将所有以下 Segment 节点包装在@987654338 中@node,只要cardinality的值相同。我希望每个cardinality 级别都发生这种情况。最后,我想将Segment 的cardinality 值移动到Cardinality 节点的type 属性。 Segment 节点的顺序必须保持。
任何帮助将不胜感激。
【问题讨论】:
-
您希望计算机能够理解“second”是“first”的子代,“third”是“second”的子代吗?
-
这些值只是举例,但我确实明白你的意思。唯一基数值在 XML 中出现的顺序最终将决定嵌套顺序。
-
这需要一些工作。顺便说一句,为什么结果中没有
Uno和Siete兄弟姐妹?它们都具有相同的“第一”基数。 -
是的,我自己也注意到了这一点,并且正在寻找“编辑”按钮。哈哈
-
您将使用哪种 XSLT 1.0 处理器?如果您的处理器支持,您可能会利用一些扩展。