1

我必须将 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

我基本上想要的是每个具有不同内容的“类型”节点的一列。“类型”没有限制。

编辑:我的实际 XSLT 将其解析为 CSV,如下所示:

Name;Name;Type
John;Lisa;Sister
John;Tom;Brother
Daniel;Peter;Father 

有谁知道如何解决我的问题?

安德烈

4

3 回答 3

2

这是一个使用xsl:key的 XSLT1.0 解决方案(感谢 Martin!),这通常是解决问题的最有效方法。本质上,您正在尝试按Type分组,因此要获得不同的家庭成员类型,您可以像这样定义一个键

<xsl:key name="Type" match="Type" use="." />

然后对于您的标题行,要实际获取不同的类型,您需要遍历所有类型,但只选择键中首先出现的记录以获取它们的给定值

<xsl:apply-templates 
   select="//Type[generate-id() = generate-id(key('Type', .)[1])]" 
   mode="header" />

(header的模式是因为Type记录会在一个单独的地方为家庭成员匹配,所以需要区分匹配的模板)

接下来,您将选择每个Person记录,并且对于每个此类记录,您将再次选择不同的类型,但这次将当前Person记录作为参数传递,以便您可以提取相关的家庭成员

<xsl:apply-templates 
   select="//Type[generate-id() = generate-id(key('Type', .)[1])]" 
   mode="family">
   <xsl:with-param name="Person" select="." />
 </xsl:apply-templates>

然后在此的匹配模板中(使用家庭模式),您可以输出该类型的相关家庭成员

这是完整的 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>&#10;</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>&#10;</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

这确实假设每个人不能有超过一个兄弟或姐妹等。

于 2012-11-08T12:22:48.937 回答
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&#xd;</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>&#xd;</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

希望这可以帮助。

于 2012-11-08T12:02:12.557 回答
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>&#10;</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>&#10;</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 中解决它,他会更快。

于 2012-11-08T12:04:21.783 回答