我需要在Visual 2013中使用XLST1.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>cardinality节点的Segment属性是顺序的,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>我尝试过几种不同的方法来转换这个文件,但是都失败了。我已经搜索和阅读了几十个帖子,但没有发现任何与我想做的事情相匹配的案例。我还尝试寻找实现目标的增量方法,例如每次只使用递归模板调用处理一个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>基本上,我想要的是将所有Segment节点封装在一个Cardinality节点中。然后,如果下一个Segment的Segment值低于当前Segment的cardinality值,我希望将所有下面的Segment节点包装在Cardinality节点中,只要cardinality值是相同的。我希望每个cardinality级别都会发生这种情况。最后,我希望将Segment的Segment值移动到Cardinality节点的type属性。必须维护Segment节点的顺序。
任何帮助都将不胜感激。
发布于 2016-04-22 16:25:59
这里是一种递归方法。它确实产生了所需的输出,至少对于给定的示例是这样的。我对此并不满意。它不是真正可靠的,也不是快速的,也不是可维护的,但至少它给了您基本的想法。(如果没有更好的)
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" />
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Segment/@cardinality" />
<xsl:template match="MessageTemplate">
<xsl:copy>
<Cardinality type="first">
<xsl:apply-templates select="Segment[1]" mode="nested" >
<xsl:with-param name="currentcardinality" select="'first'" />
</xsl:apply-templates>
</Cardinality>
</xsl:copy>
</xsl:template>
<xsl:template name="comapreNext">
<xsl:variable name="this" select="@cardinality" />
<xsl:variable name="next" select="following-sibling::Segment[1]/@cardinality" />
<xsl:choose>
<xsl:when test="$this= $next" >
<xsl:text>eq</xsl:text>
</xsl:when>
<xsl:when test="($this='first' and ($next = 'second' or $next = 'third') ) or
($this='second' and ( $next = 'third') )" >
<xsl:text>lt</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>gt</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="Segment" mode="nested">
<xsl:param name="currentcardinality"/>
<xsl:variable name="this" select="." />
<xsl:variable name="next">
<xsl:call-template name="comapreNext"/>
</xsl:variable>
<xsl:variable name="next_le" select="$next='lt' or $next = 'eq'" />
<xsl:choose>
<xsl:when test="@cardinality = $currentcardinality ">
<!-- copy Segment without cardinality -->
<xsl:apply-templates select="." />
<xsl:apply-templates select="following-sibling::Segment[1][$next_le]" mode="nested" >
<xsl:with-param name="currentcardinality" select="@cardinality" />
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise>
<Cardinality type="{@cardinality}" >
<xsl:apply-templates select="." />
<xsl:apply-templates select="following-sibling::Segment[1][$next_le]" mode="nested" >
<xsl:with-param name="currentcardinality" select="@cardinality" />
</xsl:apply-templates>
<xsl:if test="@cardinality = 'second' ">
<!-- find same cardinality but not next -->
<xsl:apply-templates select="(following-sibling::Segment[position() != 1][not(@cardinality ='third')])[1][@cardinality = $this/@cardinality]" mode="nested" >
<xsl:with-param name="currentcardinality" select="@cardinality" />
</xsl:apply-templates>
</xsl:if>
</Cardinality>
</xsl:otherwise>
</xsl:choose>
<xsl:if test="@cardinality = 'first' ">
<!-- find same cardinality but not next -->
<xsl:apply-templates select="(following-sibling::Segment[position() != 1])[@cardinality = $this/@cardinality][1]" mode="nested" >
<xsl:with-param name="currentcardinality" select="@cardinality" />
</xsl:apply-templates>
</xsl:if>
</xsl:template>
</xsl:stylesheet>生成以下输出:
<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>发布于 2016-04-21 22:14:19
这里有些东西你可以用作为你的起点。
它使用门窗分组法生成一个独立的基数列表,按照它们在源XML文档中的出现顺序。
从列表中的第一个基数开始,每个基数获取匹配段,然后递归到列表上的下一个基数,从而实现所需的嵌套。
XSLT1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:strip-space elements="*"/>
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:key name="segment-by-cardinality" match="Segment" use="@cardinality" />
<xsl:variable name="cardinalities">
<!-- generate a distinct list of cardinalities -->
<xsl:for-each select="root/MessageTemplates/MessageTemplate/Segment[count(. | key('segment-by-cardinality', @cardinality)[1]) = 1]">
<Cardinality type="{@cardinality}"/>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="cardinalities-set" select="exsl:node-set($cardinalities)/Cardinality" />
<xsl:variable name="source-doc" select="/" />
<!-- identity transform -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="MessageTemplate">
<xsl:copy>
<!-- start with the top-level cardinality -->
<xsl:apply-templates select="$cardinalities-set[1]"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Cardinality">
<xsl:variable name="type" select="@type" />
<xsl:copy>
<xsl:copy-of select="@*"/>
<!-- switch the context back to the XML source in order to use key -->
<xsl:for-each select="$source-doc">
<xsl:apply-templates select="key('segment-by-cardinality', $type)"/>
</xsl:for-each>
<!-- proceed to the next cardinality in the list -->
<xsl:apply-templates select="following-sibling::Cardinality[1]"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet> 应用于示例输入,结果如下:
<?xml version="1.0" encoding="utf-8"?>
<root>
<MessageTemplates>
<MessageTemplate>
<Cardinality type="first">
<Segment name="Uno" cardinality="first">
<value>something</value>
</Segment>
<Segment name="Siete" cardinality="first">
<value>something</value>
</Segment>
<Cardinality type="second">
<Segment name="Dos" cardinality="second">
<value>something</value>
</Segment>
<Segment name="Cinco" cardinality="second">
<value>something</value>
</Segment>
<Cardinality type="third">
<Segment name="Tres" cardinality="third">
<value>something</value>
</Segment>
<Segment name="Quatro" cardinality="third">
<value>something</value>
</Segment>
<Segment name="Seis" cardinality="third">
<value>something</value>
</Segment>
</Cardinality>
</Cardinality>
</Cardinality>
</MessageTemplate>
</MessageTemplates>
</root>请注意,这不符合“必须维护节节点的顺序”的要求。我不完全理解这个要求。如果您有一些条件来对基数的子级(即它的段和下一个更高的基数)进行排序,那么您可以在另一次传递中这样做。但是由于下一个更高的基数可以包含几个片段,其中一些可能先于当前的部分,而另一些则没有,我不太明白什么是“正确的”顺序。
https://stackoverflow.com/questions/36779806
复制相似问题