我必须将XML文件转换为CSV文件。 输入XML文件是这样的:
<Person>
<Name>John</Name>
<FamilyMembers>
<FamilyMember>
<Name>Lisa</Name>
<Type>Sister</Type>
</FamilyMember>
<FamilyMember>
<Name>Tom</Name>
<Type>Brother</Type>
</FamilyMember>
</FamilyMembers>
</Person>
<Person>
<Name>Daniel</Name>
<FamilyMembers>
<FamilyMember>
<Name>Peter</Name>
<Type>Father</Type>
</FamilyMember>
</FamilyMembers>
</Person>
最终的CSV文件应如下所示:
Name;Sister;Brother;Father
John;Lisa;Tom
Daniel;;;Peter
我基本上想要的是每个具有不同内容的“Type”节点的一列。 “类型”没有限制。
编辑:我的实际XSLT将其解析为CSV,如下所示:
Name;Name;Type
John;Lisa;Sister
John;Tom;Brother
Daniel;Peter;Father
有谁知道如何解决我的问题?
安德烈
答案 0 :(得分:2)
这是一个XSLT1.0解决方案(感谢Martin!),它使用 xsl:key ,这通常是解决问题的最有效方法。基本上你试图按类型进行分组,因此要获得不同的家庭成员类型,你可以定义一个类似的键
<xsl:key name="Type" match="Type" use="." />
然后,对于标题行,要实际获取不同类型,您将迭代所有类型,但只选择首先出现在键中的给定值的记录
<xsl:apply-templates
select="//Type[generate-id() = generate-id(key('Type', .)[1])]"
mode="header" />
(标题的模式是因为类型记录会在一个单独的位置为家庭成员匹配,所以你需要区分匹配的模板)
接下来,您将选择每个人记录,并且对于每个此类记录,您将再次选择不同的类型,但这次将当前的人员记录传递为参数,以便您可以提取相关的家庭成员
<xsl:apply-templates
select="//Type[generate-id() = generate-id(key('Type', .)[1])]"
mode="family">
<xsl:with-param name="Person" select="." />
</xsl:apply-templates>
在匹配的模板中(使用family的模式),然后您可以输出类型的相关家庭成员
这是完整的XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:key name="Type" match="Type" use="." />
<xsl:template match="/*">
<xsl:text>Name</xsl:text>
<xsl:apply-templates select="//Type[generate-id() = generate-id(key('Type', .)[1])]" mode="header" />
<xsl:text> </xsl:text>
<xsl:apply-templates select="Person" />
</xsl:template>
<xsl:template match="Person">
<xsl:value-of select="Name" />
<xsl:apply-templates select="//Type[generate-id() = generate-id(key('Type', .)[1])]" mode="family">
<xsl:with-param name="Person" select="." />
</xsl:apply-templates>
<xsl:text> </xsl:text>
</xsl:template>
<xsl:template match="Type" mode="header">
<xsl:text>;</xsl:text>
<xsl:value-of select="." />
</xsl:template>
<xsl:template match="Type" mode="family">
<xsl:param name="Person" />
<xsl:text>;</xsl:text>
<xsl:value-of select="$Person/FamilyMembers/FamilyMember[Type=current()]/Name" />
</xsl:template>
</xsl:stylesheet>
当应用于XML(假设单个根元素)时,输出以下内容
Name;Sister;Brother;Father
John;Lisa;Tom;
Daniel;;;Peter
这确实假设每个人不能拥有一个以上的兄弟或姐妹等。
答案 1 :(得分:0)
样式表
<?xml version='1.0'?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:text>Name;Sister;Brother;Father
</xsl:text>
<xsl:for-each select="Persons/Person">
<xsl:variable name="name" select="Name"/>
<xsl:variable name="others">
<xsl:value-of select="FamilyMembers/FamilyMember[Type/text()='Sister']/Name/text()"/>
<xsl:text>;</xsl:text>
<xsl:value-of select="FamilyMembers/FamilyMember[Type/text()='Brother']/Name/text()"/>
<xsl:text>;</xsl:text>
<xsl:value-of select="FamilyMembers/FamilyMember[Type/text()='Father']/Name/text()"/>
<xsl:text>
</xsl:text>
</xsl:variable>
<xsl:value-of select="concat($name,';',$others)"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
应用于XML(修改为格式良好)时:
<Persons>
<Person>
<Name>John</Name>
<FamilyMembers>
<FamilyMember>
<Name>Lisa</Name>
<Type>Sister</Type>
</FamilyMember>
<FamilyMember>
<Name>Tom</Name>
<Type>Brother</Type>
</FamilyMember>
</FamilyMembers>
</Person>
<Person>
<Name>Daniel</Name>
<FamilyMembers>
<FamilyMember>
<Name>Peter</Name>
<Type>Father</Type>
</FamilyMember>
</FamilyMembers>
</Person>
</Persons>
<强>结果
Name;Sister;Brother;Father
John;Lisa;Tom;
Daniel;;;Peter
希望这会有所帮助。
答案 2 :(得分:0)
我使用XSLT 2.0:
<xsl:stylesheet
version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs">
<xsl:param name="sep" as="xs:string" select="';'"/>
<xsl:key name="k1" match="FamilyMember" use="Type"/>
<xsl:output method="text"/>
<xsl:variable name="cols" as="xs:string*" select="('Name', distinct-values(//Person/FamilyMembers/FamilyMember/Type))"/>
<xsl:template match="/">
<xsl:value-of select="$cols" separator="{$sep}"/>
<xsl:text> </xsl:text>
<xsl:apply-templates select="//Person"/>
</xsl:template>
<xsl:template match="Person">
<xsl:value-of select="Name"/>
<xsl:variable name="cells" as="xs:string*" select="
for $col in $cols[position() gt 1] return (key('k1', $col, current())/Name, '')[1]"/>
<xsl:sequence select="if (not(empty($cells))) then concat($sep, string-join($cells, $sep)) else ()"/>
<xsl:text> </xsl:text>
</xsl:template>
</xsl:stylesheet>
变换
<Persons>
<Person>
<Name>John</Name>
<FamilyMembers>
<FamilyMember>
<Name>Lisa</Name>
<Type>Sister</Type>
</FamilyMember>
<FamilyMember>
<Name>Tom</Name>
<Type>Brother</Type>
</FamilyMember>
</FamilyMembers>
</Person>
<Person>
<Name>Daniel</Name>
<FamilyMembers>
<FamilyMember>
<Name>Peter</Name>
<Type>Father</Type>
</FamilyMember>
</FamilyMembers>
</Person>
</Persons>
到
Name;Sister;Brother;Father
John;Lisa;Tom;
Daniel;;;Peter
我可能稍后尝试将其转换为XSLT 1.0解决方案,但我想如果Tim C已经尝试在XSLT 1.0中解决它,他会更快。