用于动态属性分配的XSLT变换

时间:2020-10-30 05:14:26

标签: xml xslt

我正在尝试使用Java将xml转换为csv。我编写了一个使用xslt生成csv文件的程序,该程序可用于以下程序:

<xsl:text>PART_ID,TITLE,ITEM_NUMBER,SUBASSEMBLY_ITEM,QUANTITY</xsl:text>
<xsl:text>&#xA;</xsl:text>
<xsl:for-each select="//row">
  <xsl:call-template name="CsvEscape"><xsl:with-param name="value" select="normalize-space(PART_ID)"/></xsl:call-template>
  <xsl:text>,</xsl:text>
  <xsl:call-template name="CsvEscape"><xsl:with-param name="value" select="normalize-space(TITLE)"/></xsl:call-template>
  <xsl:text>,</xsl:text>
  <xsl:call-template name="CsvEscape"><xsl:with-param name="value" select="normalize-space(ITEM_NUMBER)"/></xsl:call-template>
  <xsl:text>,</xsl:text>
  <xsl:call-template name="CsvEscape"><xsl:with-param name="value" select="normalize-space(SUBASSEMBLY_ITEM)"/></xsl:call-template>
  <xsl:text>,</xsl:text>
  <xsl:call-template name="CsvEscape"><xsl:with-param name="value" select="normalize-space(QUANTITY)"/></xsl:call-template>
  <xsl:text>&#xA;</xsl:text>
</xsl:for-each>

使用CsvEscape函数,生成的表格的格式正确,并且数据中的任何逗号在CSV文件中的格式正确。

现在我正在尝试使它动态化,因此它可以在不直接指定表头的情况下适用于任何XML。

  <xsl:template match="/">
    <xsl:for-each select="//row[1]">
        <xsl:for-each select="*">
                <xsl:call-template name="CsvEscape"><xsl:with-param name="value" select="normalize-space(name(.))"/></xsl:call-template>
                <xsl:text>, </xsl:text>
        </xsl:for-each>
    </xsl:for-each>
    <xsl:text>&#xA;</xsl:text>
    <xsl:for-each select="//row">
        <xsl:for-each select="*">
                    <xsl:call-template name="CsvEscape"><xsl:with-param name="value" select="normalize-space(.)"/></xsl:call-template>
                <xsl:text>, </xsl:text>
        </xsl:for-each>
        <xsl:text>&#xA;</xsl:text>
    </xsl:for-each>
</xsl:template>

我的XML:

<row>
    <PART_ID>15-00002-01</PART_ID>
    <TITLE>Shell Assembly</TITLE>
    <ITEM_NUMBER>4</ITEM_NUMBER>
    <SUBASSEMBLY_ITEM>18-00004-01 - Polyurethane, 4Ply, Hex Cut White Patches</SUBASSEMBLY_ITEM>
    <QUANTITY>15.000000000000</QUANTITY>
</row>

注意:xml中的项目中也可以包含“,”,因此我需要通过CsvEscape运行它才能正确显示。

必需的输出是:

    PART_ID,TITLE,ITEM_NUMBER,SUBASSEMBLY_ITEM,QUANTITY
15-20002-01,Shell Assembly,1,"18-20003-01 - Polyurethane, 4Ply, Hex Cut Black Patches",16.000000000000

这生成了带有动态生成标题的CSV文件。数据也正在被识别,但是没有通过CsvEscape函数传递,如果我的数据中有逗号,这会弄乱我的CSV文件。

我该如何解决?

1 个答案:

答案 0 :(得分:1)

假设此输入XML

<?xml version="1.0" encoding="UTF-8"?>
<rows>
    <row>
        <cell>PART_ID</cell>
        <cell>TITLE</cell>
        <cell>ITEM_NUMBER</cell>
        <cell>SUBASSEMBLY_ITEM</cell>
        <cell>QUANTITY</cell>
    </row>
    <row>
        <cell>id1</cell>
        <cell> some title </cell>
        <cell>1</cell>
        <cell>y;x,z</cell>
        <cell>20</cell>
    </row>
    <row>
        <cell>id2</cell>
        <cell> another title </cell>
        <cell>2</cell>
        <cell>q/r|s</cell>
        <cell>10</cell>
    </row>
</rows>

这是一种基于参数化的基于XPath的解决方案,用于一般生成CSV输出:

<?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="3.0">
    
    <xsl:output method="text" encoding="UTF-8"/>
    
    
    <xsl:template match="/">
        <xsl:sequence select="
            let $escapeChar := '&quot;',
                $newLine    := '&#xA;',
                $separator  := ','
                return //row ! (string-join(* ! string-join($escapeChar||normalize-space(.)||$escapeChar),$separator),$newLine)
            "/>
    </xsl:template>

</xsl:stylesheet>

输出:

"PART_ID","TITLE","ITEM_NUMBER","SUBASSEMBLY_ITEM","QUANTITY" 
 "id1","some title","1","y;x,z","20" 
 "id2","another title","2","q/r|s","10" 

如果您想要更多基于XSLT的解决方案,建议使用模板:

<xsl:variable name="escapeChar" select="'&quot;'" as="xs:string"/>
<xsl:variable name="separator" select="','" as="xs:string"/>
<xsl:variable name="newLine" select="'&#xA;'" as="xs:string"/>

<!-- identity template or shallow-skip -->
<xsl:mode on-no-match="shallow-skip"/>

<xsl:template match="//row">
    <xsl:apply-templates select="*"/>
    <xsl:value-of select="$newLine"/>
</xsl:template>

<xsl:template match="*[parent::row]">
    <xsl:value-of select="$escapeChar"/>
    <xsl:value-of select="."/>
    <xsl:value-of select="$escapeChar"/>
    <xsl:if test="not(position()=last())">
        <xsl:value-of select="$separator"/>
    </xsl:if>
</xsl:template>