使用一个 XSL 转换将 CSV 文件转换为分层 XML
Converting CSV Files to Hierarchical XML Using One XSL Transformation
我正在尝试将逗号分隔列表转换为具有层次结构的 XML 文件。为此,我单独使用 XSLT,最好使用一次转换。前面有一个类似的例子,但它没有深入到创建子元素的深度,我认为这是这种转换过程中的一个常见问题,据我所知还没有明确的答案。
类似例子:
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 来区分不同的部门?
您还没有真正解释嵌套元素的标准是什么,但正如评论中已经指出的那样,您可以以任何方式转换您首先创建的平面 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>
我正在尝试将逗号分隔列表转换为具有层次结构的 XML 文件。为此,我单独使用 XSLT,最好使用一次转换。前面有一个类似的例子,但它没有深入到创建子元素的深度,我认为这是这种转换过程中的一个常见问题,据我所知还没有明确的答案。
类似例子:
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 来区分不同的部门?
您还没有真正解释嵌套元素的标准是什么,但正如评论中已经指出的那样,您可以以任何方式转换您首先创建的平面 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>