XSL:XML到CSV转换,解析属性

时间:2018-05-02 15:06:17

标签: xml xslt

我正在将XML文件转换为XSLT 3.0中的CSV文件,但仅处理某些节点。其中一个节点是一个由空格分隔的多个值的属性,我想将这些值中的每一个分解为目标CSV文件中的单独一行。

示例源XML文件:

<corpus>
 <deposition>
    <deposition-number n="dep1"/>
    <text>
      <seg n="seg1">Lorem ipsum dolor sit amet, <persName name="personA" ana="foo1 foo2">some foo person</persName> 
        adipiscing elit. Vivamu ultrices consequat facilisis. 
        Suspendisse a odio in lobortis. Aenean <persName name="personG" ana="foo2">some foo person</persName>
        non dui scelerisque, rutrum est at, cursus sem.</seg>
      <seg n="seg2">Ut pharetra <persName name="personF" ana="foo9">some foo person</persName>bibendum ipsum, portitor 
        velit pharetra quis. Aeneano purus. Praesent 
        aliquam viverra tellus.</seg>
     </text>
  </deposition>
  <deposition>
    <deposition-number n=""dep2"/>
    <text>
     <seg n="seg3">Curabitur pulvinar leo eget. Orci varius 
        natoque penatibus et magnis <persName name="personD" ana="foo2 foo3 foo8">some foo person</persName>montes, 
        nascetur ridiculus mus.</seg>
    </text>
  </deposition>
</corpus>

目前我可以在CSV中输出这个令人不满意的结果,其中@ana位于最后一个字段中:

 dep1~seg1~personA~foo1 foo2
 dep1~seg1~personG~foo2
 dep1~seg2~personF~foo9
 dep1~seg3~personD~foo2 foo3 foo8

使用如下所示的XSL:

<xsl:template match="/">
    <xsl:for-each select="persName">
        <xsl:value-of select="ancestor::deposition-number/@n"/>
        <xsl:text>~</xsl:text>
        <xsl:value-of select="ancestor::seg/@n"/>
        <xsl:text>~</xsl:text>
        <xsl:value-of select="@name"/>
        <xsl:text>~</xsl:text>
        <xsl:value-of select="@ana"/>
        <xsl:text>&#10;</xsl:text>
    </xsl:for-each>
</xsl:template>

但我想输出这个,@ana被解析为不同的行:

 dep1~seg1~personA~foo1
 dep1~seg1~personA~foo2
 dep1~seg1~personG~foo2
 dep1~seg2~personF~foo9
 dep1~seg3~personD~foo2
 dep1~seg3~personD~foo3
 dep1~seg3~personD~foo8

我已经尝试过标记化,但如果它能够解决这个问题,我不知道该怎么做。我不知道如何解析@ana并循环它。

提前致谢。

2 个答案:

答案 0 :(得分:3)

请尝试使用此模板,该模板使用tokenize拆分属性并为每个值输出一行

<xsl:template match="/">
    <xsl:for-each select="//persName">
        <xsl:variable name="pers" select="." />
        <xsl:for-each select="tokenize(@ana, ' ')">
            <xsl:value-of select="$pers/ancestor::deposition/deposition-number/@n"/>
            <xsl:text>~</xsl:text>
            <xsl:value-of select="$pers/ancestor::seg/@n"/>
            <xsl:text>~</xsl:text>
            <xsl:value-of select="$pers/@name"/>
            <xsl:text>~</xsl:text>
            <xsl:value-of select="."/>
            <xsl:text>&#10;</xsl:text>
        </xsl:for-each>
    </xsl:for-each>
</xsl:template>

(请注意,deposition-number不是XML中persName的祖先)

或者,为了简化它,试试这个......

<xsl:template match="/">
    <xsl:for-each select="//persName">
        <xsl:variable name="pers" select="." />
        <xsl:for-each select="tokenize(@ana, ' ')">
            <xsl:value-of select="($pers/ancestor::deposition/deposition-number/@n, $pers/ancestor::seg/@n, $pers/@name, .)" separator="~"/>
            <xsl:text>&#10;</xsl:text>
        </xsl:for-each>
    </xsl:for-each>
</xsl:template>

答案 1 :(得分:2)

使用XSLT 3,您可以标记值并将它们推送到模板:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    version="3.0">

  <xsl:output method="text"/>

  <xsl:template match="/">
      <xsl:apply-templates select="//persName"/>
  </xsl:template>

  <xsl:template match="persName">
     <xsl:apply-templates select="tokenize(@ana, '\s+')">
         <xsl:with-param name="data" select="ancestor::deposition/deposition-number/@n, ancestor::seg/@n, @name"/>
     </xsl:apply-templates>
  </xsl:template>

  <xsl:template match=".[. instance of xs:string]">
      <xsl:param name="data"/>
      <xsl:value-of select="$data, ." separator="~"/>
      <xsl:text>&#10;</xsl:text>
  </xsl:template>

</xsl:stylesheet>

https://xsltfiddle.liberty-development.net/bdxtpS/1