【问题标题】:Converting CSV Files to Hierarchical XML Using One XSL Transformation使用一个 XSL 转换将 CSV 文件转换为分层 XML
【发布时间】:2017-06-23 00:14:40
【问题描述】:

我正在尝试将逗号分隔列表转换为具有层次结构的 XML 文件。为此,我单独使用 XSLT,最好使用一次转换。前面有一个类似的例子,但它没有深入创建子元素,我认为这是这种转换过程中的一个常见问题,据我所知一直没有明确的答案。

类似的例子: XSLT 2.0 to convert CSV to XML format

CSV 示例

ClaimRef,HandlerRef,ClaimType,Date,Area,SettleDate,ClaimStatus,ClaimantName
1,1/1,Liability,08-12-2013,US,23-05-2014,Closed,Mark
2,1/2,Liability,08-10-2013,UK,23-02-2014,Closed,John

所需的 XML 输出格式(此处不同,因为它包含子元素)

<Claims>
 <Claim>
  <ClaimRef></ClaimRef>
  <HandlerRef></HandlerRef>
  <ClaimType></ClaimType>
  <Date></Date>
  <Area></Area>
  <SettleDate></SettleDate>
  <ImportantDevision>
      <ClaimStatus></ClaimStatus>
      <ClaimantName></ClaimantName>
  </ImportantDivision>
 </Claim>
</Claims>

没有子元素的工作 XSLT 2.0 版

<xsl:param name="inputCsv"/>
<xsl:template match="/" name="csv2xml">
    <Claims>
         <xsl:variable name="csv" select="unparsed-text($csv-uri, $csv-encoding)"/>
                <!--Get Header-->
                <xsl:variable name="header-tokens" as="xs:string*">
                    <xsl:analyze-string select="$csv" regex="\r\n?|\n">
                        <xsl:non-matching-substring>
                            <xsl:if test="position()=1">
                                <xsl:copy-of select="tokenize(.,',')"/>                                        
                            </xsl:if>
                        </xsl:non-matching-substring>
                    </xsl:analyze-string>
                </xsl:variable>                    
                <xsl:analyze-string select="$csv" regex="\r\n?|\n">
                    <xsl:non-matching-substring>
                        <xsl:if test="not(position()=1)">
                            <Claim>
                                <xsl:for-each select="tokenize(.,',')">
                                    <xsl:variable name="pos" select="position()"/>
                                    <xsl:element name="{$header-tokens[$pos]}">
                                        <xsl:value-of select="."/>
                                    </xsl:element>
                                </xsl:for-each>
                            </Claim>
                        </xsl:if>
                    </xsl:non-matching-substring>
                </xsl:analyze-string>
    </Claims>
</xsl:template>

然后我将拥有一个虚拟 XML 文件,以欺骗 XSL 来转换我的 CSV 文件。 也许一个更好的问题是如何在创建标记名称、属性、ID 等之前仅使用 XSLT 来区分各个部分?

【问题讨论】:

  • 不确定您的问题到底是什么。您的样式表将连续处理所有单元格。如果您想将其中一些放在包装器元素中,您必须告诉样式表哪些是 - 按名称或按位置。无法从输入中推断出此信息。
  • 这里的论文saxonica.com/papers/ideadb-1.1/mhk-paper.xml 讨论了一个类似的问题,它可能会给你一些想法。请注意,使用 XSLT 2.0 不需要 XML 源文档:您可以在命名模板处启动处理。

标签: xml csv xslt xslt-2.0 hierarchy


【解决方案1】:

您还没有真正解释嵌套元素的标准是什么,但正如评论中已经指出的那样,您可以以任何方式转换您首先创建的平面 XML。以下假设您只想嵌套以名称 Claim 开头的相邻元素:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" version="2.0">

    <xsl:param name="csv-uri" as="xs:string" select="'test2017062301.txt'"/>
    <xsl:param name="csv-encoding" as="xs:string" select="'Windows-1252'"/>

    <xsl:output indent="yes"/>

    <xsl:template match="@* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="/" name="csv2xml">
        <Claims>
            <xsl:variable name="csv" select="unparsed-text($csv-uri, $csv-encoding)"/>
            <xsl:variable name="flat-xml">
                <!--Get Header-->
                <xsl:variable name="header-tokens" as="xs:string*">
                    <xsl:analyze-string select="$csv" regex="\r\n?|\n">
                        <xsl:non-matching-substring>
                            <xsl:if test="position() = 1">
                                <xsl:copy-of select="tokenize(., ',')"/>
                            </xsl:if>
                        </xsl:non-matching-substring>
                    </xsl:analyze-string>
                </xsl:variable>
                <xsl:analyze-string select="$csv" regex="\r\n?|\n">
                    <xsl:non-matching-substring>
                        <xsl:if test="not(position() = 1)">
                            <Claim>
                                <xsl:for-each select="tokenize(., ',')">
                                    <xsl:variable name="pos" select="position()"/>
                                    <xsl:element name="{$header-tokens[$pos]}">
                                        <xsl:value-of select="."/>
                                    </xsl:element>
                                </xsl:for-each>
                            </Claim>
                        </xsl:if>
                    </xsl:non-matching-substring>
                </xsl:analyze-string>
            </xsl:variable>
            <xsl:apply-templates select="$flat-xml/*"/>
        </Claims>
    </xsl:template>

    <xsl:template match="Claim">
        <xsl:copy>
            <xsl:for-each-group select="*" group-adjacent="starts-with(local-name(), 'Claim')">
                <xsl:choose>
                    <xsl:when test="current-grouping-key() and current-group()[2]">
                        <ClaimDivision>
                            <xsl:copy-of select="current-group()"/>
                        </ClaimDivision>
                    </xsl:when>
                    <xsl:otherwise>
                        <xsl:copy-of select="current-group()"/>
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:for-each-group>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

您的示例输入的结果是

<?xml version="1.0" encoding="UTF-8"?>
<Claims>
   <Claim>
      <ClaimRef>1</ClaimRef>
      <HandlerRef>1/1</HandlerRef>
      <ClaimType>Liability</ClaimType>
      <Date>08-12-2013</Date>
      <Area>US</Area>
      <SettleDate>23-05-2014</SettleDate>
      <ClaimDivision>
         <ClaimStatus>Closed</ClaimStatus>
         <ClaimantName>Mark</ClaimantName>
      </ClaimDivision>
   </Claim>
   <Claim>
      <ClaimRef>2</ClaimRef>
      <HandlerRef>1/2</HandlerRef>
      <ClaimType>Liability</ClaimType>
      <Date>08-10-2013</Date>
      <Area>UK</Area>
      <SettleDate>23-02-2014</SettleDate>
      <ClaimDivision>
         <ClaimStatus>Closed</ClaimStatus>
         <ClaimantName>John</ClaimantName>
      </ClaimDivision>
   </Claim>
</Claims>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-02-13
    • 2013-06-23
    • 2022-01-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-31
    相关资源
    最近更新 更多