使用xslt将XML转换为CSV - 使用空白处理缺少的元素

时间:2018-02-21 09:22:51

标签: xml csv xslt

我正在尝试为XML创建通用xslt到CSV。

当特定子项中不可用的元素不会产生空白值且节点

时,会遇到问题

是否有可能为不同的xml实现通用xslt,它具有相似的树结构,只有元素节点不同

Root- Child - Element1,Element2       孩子 - Element3       孩子 - Element4

示例输入XML

<?xml version="1.0" encoding="UTF-8"?>
<queryResponse>

  <User>
    <Id>rti</Id>

  </User>
  <User>
    <Id>qwe</Id>
    <EmployeeNumber>emp1</EmployeeNumber>
    <IsActive>false</IsActive>
  </User>
  <User>
    <Id>Abc</Id>
    <IsActive>false</IsActive>
  </User>
  <User>
    <Id>123</Id>
    <EmployeeNumber>emp4</EmployeeNumber>
    <IsActive>false</IsActive>

  </User>
</queryResponse>

预期产出

&#13;
&#13;
"Id","EmployeeNumber","IsActive"
"rti","",""
"qwe","emp1","false"
"Abc","","false"
"123","emp4","false"
&#13;
&#13;
&#13;

尝试过XSLT

&#13;
&#13;
   
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" />

<xsl:param name="delim" select="','" />
<xsl:param name="break" select="'&#xA;'" /><!-- xA = NL, xD = CR -->
<xsl:param name="colnames" select="'y'"/>

<xsl:strip-space elements="*" />


<xsl:template match="/*/child::*">
<!--headerline-->
<xsl:if test="$colnames = 'y'">
 <!-- <xsl:if test="position() = 1"> -->
    <xsl:for-each select="child::*">
	<xsl:text>"</xsl:text> 
	<xsl:value-of select="name()"/>
	<xsl:text>"</xsl:text>  
             <xsl:if test="position() != last()">
	           <xsl:value-of select="$delim"/>
             </xsl:if>
      </xsl:for-each>
      <!-- hardcode version newline -->
	<!--<xsl:text>&#xa;</xsl:text>-->
      <!-- linebreak, nicer -->
      <xsl:value-of select="$break" />
<!--  </xsl:if>  -->
</xsl:if>


<!--dataline-->
<xsl:for-each select="child::*">

<xsl:if test="position() != last()">
      <xsl:text>"</xsl:text> 
      <xsl:value-of select="normalize-space(.)"/>
      <xsl:text>"</xsl:text> 
	  <xsl:value-of select="$delim" />
</xsl:if>

<xsl:if test="position()  = last()">
 <xsl:text>"</xsl:text>
<xsl:value-of select="normalize-space(.)"/>
 <xsl:text>"</xsl:text>
	  <xsl:value-of select="$break" />
</xsl:if>

</xsl:for-each>
</xsl:template>

</xsl:stylesheet>
&#13;
&#13;
&#13;

结果输出

&#13;
&#13;
"Id"
"rti"
"qwe","emp1","false"
"Abc","false"
"123","emp4","false"
&#13;
&#13;
&#13;

1 个答案:

答案 0 :(得分:0)

您需要定义或检测唯一列名称(即第三级元素名称),然后处理每个第二级元素以检查它是否具有这样的列,如果不是输出空字符串。

如果您不局限于XSLT 1但可以使用XSLT 3(由Saxon 9.8或Altova 2017/2018或Exselt 1.1支持),您可以以紧凑的方式执行此操作:

<?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"
    xmlns:mf="http://example.com/mf"
    exclude-result-prefixes="xs mf"
    version="3.0">

  <xsl:param name="sep" as="xs:string" select="','"/>
  <xsl:param name="lf" as="xs:string" select="'&#10;'"/>
  <xsl:param name="quote" as="xs:string" select="'&quot;'"/>

  <xsl:output method="text"/>
  <xsl:strip-space elements="*"/>

  <xsl:function name="mf:quote" as="xs:string">
      <xsl:param name="input" as="xs:string"/>
      <xsl:sequence select="$quote || replace($input, $quote, '$0$0') || $quote"/>
  </xsl:function>

  <xsl:param name="cols" as="xs:QName*" select="distinct-values(/*/*/*/node-name())"/>

  <xsl:template match="/">
      <xsl:value-of select="$cols!mf:quote(string())" separator="{$sep}"/>
      <xsl:value-of select="$lf"/>
      <xsl:apply-templates select="*/*"/>
  </xsl:template>

  <xsl:template match="*">
      <xsl:value-of select="for $col in $cols return mf:quote((*[node-name() eq $col], '')[1])" separator="{$sep}"/>
      <xsl:value-of select="$lf"/>
  </xsl:template>

</xsl:stylesheet>

http://xsltfiddle.liberty-development.net/jyyiVhr的在线示例。

XSLT 2版本位于http://xsltransform.hikmatu.com/bFukv8h,它只使用for .. return ..代替!concat代替||

<?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"
    xmlns:mf="http://example.com/mf"
    exclude-result-prefixes="xs mf"
    version="2.0">

  <xsl:param name="sep" as="xs:string" select="','"/>
  <xsl:param name="lf" as="xs:string" select="'&#10;'"/>
  <xsl:param name="quote" as="xs:string" select="'&quot;'"/>

  <xsl:output method="text"/>
  <xsl:strip-space elements="*"/>

  <xsl:function name="mf:quote" as="xs:string">
      <xsl:param name="input" as="xs:string"/>
      <xsl:sequence select="concat($quote, replace($input, $quote, '$0$0'), $quote)"/>
  </xsl:function>

  <xsl:param name="cols" as="xs:QName*" select="distinct-values(/*/*/*/node-name(.))"/>

  <xsl:template match="/">
      <xsl:value-of select="for $col in $cols return mf:quote(string($col))" separator="{$sep}"/>
      <xsl:value-of select="$lf"/>
      <xsl:apply-templates select="*/*"/>
  </xsl:template>

  <xsl:template match="*">
      <xsl:value-of select="for $col in $cols return mf:quote((*[node-name(.) eq $col], '')[1])" separator="{$sep}"/>
      <xsl:value-of select="$lf"/>
  </xsl:template>

</xsl:stylesheet>