XSLT - 使用 XSLT 1 的 XML 到 CSV 动态模板



>我正在尝试将XML解析为平面文件。在我在SO找到的关于这个主题的许多主题中,这两个主题都是我希望完成的部分内容。

使用 XSLT 帮助的 XML 到 CSV

使用 XSLT 的 XML 到 CSV

示例 XML

<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
<env:Body>
<wd:Get_Schools_Response wd:version="v29.1" xmlns:wd="urn:com.workday/bsvc">
<wd:Response_Filter>
<wd:Page>1</wd:Page>
<wd:Count>50</wd:Count>
</wd:Response_Filter>
<wd:Response_Group>
<wd:Include_Reference>0</wd:Include_Reference>
</wd:Response_Group>
<wd:Response_Results>
<wd:Total_Results>19448</wd:Total_Results>
<wd:Total_Pages>389</wd:Total_Pages>
<wd:Page_Results>50</wd:Page_Results>
<wd:Page>1</wd:Page>
</wd:Response_Results>
<wd:Response_Data>
<wd:School>
<wd:School_Data>
<wd:ID>Chonnam_National_University_Yosu</wd:ID>
<wd:School_Name>Chonnam National University (Yosu)</wd:School_Name>
<wd:Country_Reference>
<wd:ID wd:type="WID">7a5a2aadf9d34086a2bfbfd408bc28da</wd:ID>
<wd:ID wd:type="ISO_3166-1_Alpha-2_Code">KR</wd:ID>
<wd:ID wd:type="ISO_3166-1_Alpha-3_Code">KOR</wd:ID>
<wd:ID wd:type="ISO_3166-1_Numeric-3_Code">410</wd:ID>
</wd:Country_Reference>
<wd:Inactive>0</wd:Inactive>
</wd:School_Data>
</wd:School>
<wd:School>
<wd:School_Data>
<wd:ID>Asian_University_Of_Science_Technology</wd:ID>
<wd:School_Name>Asian University of Science &amp; Technology</wd:School_Name>
<wd:Country_Reference>
<wd:ID wd:type="WID">873d0f604e3b458c990cb4d83a5c0f14</wd:ID>
<wd:ID wd:type="ISO_3166-1_Alpha-2_Code">TH</wd:ID>
<wd:ID wd:type="ISO_3166-1_Alpha-3_Code">THA</wd:ID>
<wd:ID wd:type="ISO_3166-1_Numeric-3_Code">764</wd:ID>
</wd:Country_Reference>
<wd:Inactive>0</wd:Inactive>
</wd:School_Data>
</wd:School>
<wd:School>
<wd:School_Data>
<wd:ID>Groep_T_Leuven</wd:ID>
<wd:School_Name>Groep T Leuven</wd:School_Name>
<wd:Country_Reference>
<wd:ID wd:type="WID">a04ea128f43a42e59b1e6a19e8f0b374</wd:ID>
<wd:ID wd:type="ISO_3166-1_Alpha-2_Code">BE</wd:ID>
<wd:ID wd:type="ISO_3166-1_Alpha-3_Code">BEL</wd:ID>
<wd:ID wd:type="ISO_3166-1_Numeric-3_Code">56</wd:ID>
</wd:Country_Reference>
<wd:Inactive>0</wd:Inactive>
</wd:School_Data>
</wd:School>
<wd:School>
<wd:School_Data>
<wd:ID>Tohono_O_Odham_Community_College</wd:ID>
<wd:School_Name>Tohono O'Odham Community College</wd:School_Name>
<wd:Country_Region_Reference>
<wd:ID wd:type="WID">c7b20b0d4bc04711a00900569e9afabd</wd:ID>
<wd:ID wd:type="Country_Region_ID">USA-AZ</wd:ID>
<wd:ID wd:type="ISO_3166-2_Code">AZ</wd:ID>
</wd:Country_Region_Reference>
<wd:Country_Reference>
<wd:ID wd:type="WID">bc33aa3152ec42d4995f4791a106ed09</wd:ID>
<wd:ID wd:type="ISO_3166-1_Alpha-2_Code">US</wd:ID>
<wd:ID wd:type="ISO_3166-1_Alpha-3_Code">USA</wd:ID>
<wd:ID wd:type="ISO_3166-1_Numeric-3_Code">840</wd:ID>
</wd:Country_Reference>
<wd:Inactive>0</wd:Inactive>
</wd:School_Data>
</wd:School>
</wd:Response_Data>
</wd:Get_Schools_Response>
</env:Body>
</env:Envelope>
<xsl:stylesheet version="1.0" 

在第一个链接的情况下,我得到以下内容:

1|50|0|19448|389|50|1|Chonnam_National_University_Yosu|Chonnam National University (Yosu)|7a5a2aadf9d34086a2bfbfd408bc28da|KR|KOR|410|0|Asian_University_Of_Science_Technology|Asian University of Science & Technology|873d0f604e3b458c990cb4d83a5c0f14|TH|THA|764|0|Groep_T_Leuven|Groep T Leuven|a04ea128f43a42e59b1e6a19e8f0b374|BE|BEL|56|0|Tohono_O_Odham_Community_College|Tohono O'Odham Community College|c7b20b0d4bc04711a00900569e9afabd|USA-AZ|AZ|bc33aa3152ec42d4995f4791a106ed09|US|USA|840|0

这是一个很好的解决方案,因为它向下钻取到每个子节点并放入分隔符,但不知道前一个祖先的子节点。 此外,我不希望页面/结果/total_pages信息过来。我添加了标准模板覆盖,但这没有做任何事情。

<xsl:template match="text()|@*">
<!--<xsl:value-of select="."/>
Do nothing -->
</xsl:template>

在第二个情况下:

ID|School_Name|Country_Reference|Inactive|Country_Region_Reference
Chonnam_National_University_Yosu|Chonnam National University (Yosu)|7a5a2aadf9d34086a2bfbfd408bc28daKRKOR410|0|
Asian_University_Of_Science_Technology|Asian University of Science & Technology|873d0f604e3b458c990cb4d83a5c0f14THTHA764|0|
Groep_T_Leuven|Groep T Leuven|a04ea128f43a42e59b1e6a19e8f0b374BEBEL56|0|
Tohono_O_Odham_Community_College|Tohono O'Odham Community College|bc33aa3152ec42d4995f4791a106ed09USUSA840|0|c7b20b0d4bc04711a00900569e9afabdUSA-AZAZ

在第二个示例中,它不够动态,它不会在子值之间添加条形图。我尝试做这样的事情:

<xsl:key name="field" match="/*/*/*/*/*/*/*/child::*" use="local-name()"/>
<!-- variable containing the first occurrence of each field -->
<xsl:variable name="allFields"
select="/*/*/*/*/*/*/*/child::*[generate-id()=generate-id(key('field', local-name())[1])]" />

这会产生类似的东西:

ID
Chonnam_National_University_Yosu
Asian_University_Of_Science_Technology
Groep_T_Leuven
Tohono_O_Odham_Community_College

我希望的是动态钻取所有子项和孙子等,并生成一个带有所有值分隔符的平面文件,即使以前的节点没有这些值,并用换行符完成每一行。此外,从第一个结果中删除 1|50|0|19448|389|50|1:

Chonnam_National_University_Yosu|Chonnam National University (Yosu)|7a5a2aadf9d34086a2bfbfd408bc28da||||KR|KOR|410|0
Asian_University_Of_Science_Technology|Asian University of Science & Technology|873d0f604e3b458c990cb4d83a5c0f14||||TH|THA|764|0
Groep_T_Leuven|Groep T Leuven|a04ea128f43a42e59b1e6a19e8f0b374||||BE|BEL|56|0
Tohono_O_Odham_Community_College|Tohono O'Odham Community College|c7b20b0d4bc04711a00900569e9afabd|USA-AZ|AZ|bc33aa3152ec42d4995f4791a106ed09|US|USA|840|0

我正在使用 XSLT,但我愿意接受有关其他工具或方法的建议。

我有一个在 XSLT 1.0 中为类似问题创建的样式表。(我找不到它的链接以供参考。我相信在我提交答案之前,这个问题就被删除了。幸运的是我保存了它。

我对它进行了一些更改,似乎可以生成您正在寻找的输出。

根据您的示例和描述,这些是我认为的要求应该:

  • 作为wd:School_Data后代的所有文本都需要输出。
  • 每个wd:School_Data都有一行。
  • 文本所属的列/字段基于:1) 元素名称或 2)wd:type属性的值和父属性的名称(如果指定了wd:type属性)。
  • 所有行都应该为每列提供一个条目,即使该行没有该列的值。

我需要做的第一件事是确定唯一列将是什么。

为此,我首先创建了一个xsl:key(在样式表中名为cols),它匹配wd:School_Data的后代并包含文本的所有元素。键使用了本地名称(不带前缀)和用~分隔的wd:type属性的组合。例如,元素<wd:ID>Groep_T_Leuven</wd:ID>将具有键ID~<wd:ID wd:type="ISO_3166-1_Alpha-2_Code">BE</wd:ID>将具有键ID~ISO_3166-1_Alpha-2_Code

第二步是创建一个变量(allCols),其中包含所有键(local-name()/wd:type组合)的唯一列表,用|分隔。这个变量将允许我递归处理它,以便保证每一行都有每列的条目,并且顺序始终相同。该值是通过使用模式模板(模式getCols)仅处理每个键(Muenchian 分组)中的第一个节点来创建的。

由于第一行(标题)中的列条目需要与allCols中的值不同,因此我创建了另一个名为header的变量。这个变量的值的创建类似于allCols,但它要么只使用本地名称,要么使用父本地名称和wd:type属性的值(取决于属性是否存在)。它还使用了模式header的模式化模板。您会注意到我在此变量中使用了substring($temp,2)。这是为了从$temp开头删除不需要的|

为了将处理限制为仅wd:School_Data,我匹配了根元素(/*),并且只选择了xsl:for-each中的wd:School_Data。我还输出header变量的值。

处理wd:School_Data元素时,我将命名模板称为outputFields。我传递了一个包含allCols变量的参数。

outputFields模板是进行大部分处理的地方。首先,创建四个变量。

前两个变量fieldleftToProcess是第一个字段和其余字段。

后两个变量elemNameelemType是分隔的元素名称和类型值。

xsl:choose中,选择匹配元素的值。选择方式取决于是否存在类型值。

xsl:if是模板的递归部分。如果leftToProcess中包含字段,则会再次调用模板,并将leftToProcess作为toProcess参数值。

XSLT 1.0(此处的工作示例:http://xsltfiddle.liberty-development.net/pPgCcor/1)

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:wd="urn:com.workday/bsvc">
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>
<xsl:key name="cols" match="wd:School_Data//*[text()]" use="concat(local-name(),'~',@wd:type)"/>
<xsl:variable name="allCols">
<xsl:apply-templates 
select="//wd:School_Data//*[text()][count(.|key('cols',concat(local-name(),'~',@wd:type))[1])=1]"
mode="getCols"
/> 
</xsl:variable>
<xsl:variable name="header">
<xsl:variable name="temp">
<xsl:apply-templates 
select="//wd:School_Data//*[text()][count(.|key('cols',concat(local-name(),'~',@wd:type))[1])=1]"
mode="header"
/>      
</xsl:variable>
<xsl:value-of select="substring($temp,2)"/>
</xsl:variable>
<xsl:template match="/*">
<xsl:message><xsl:value-of select="$allCols"/></xsl:message>
<xsl:value-of select="concat($header,'&#xA;')"/>
<xsl:for-each select="//wd:School_Data">
<xsl:call-template name="outputFields">
<xsl:with-param name="toProcess" select="$allCols"/>
</xsl:call-template>
<xsl:text>&#xA;</xsl:text>
</xsl:for-each>
</xsl:template>
<xsl:template name="outputFields">
<xsl:param name="toProcess"/>
<xsl:variable name="field" select="substring-before($toProcess, '|')"/>
<xsl:variable name="leftToProcess" select="substring-after($toProcess, '|')"/>
<xsl:variable name="elemName" select="substring-before($field,'~')"/>
<xsl:variable name="elemType" select="substring-after($field,'~')"/>
<xsl:choose>
<xsl:when test="$elemType">
<xsl:value-of select=".//*[local-name()=$elemName and @wd:type=$elemType]"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select=".//*[local-name()=$elemName]"/>        
</xsl:otherwise>
</xsl:choose>
<xsl:if test="$leftToProcess">
<xsl:text>|</xsl:text>
<xsl:call-template name="outputFields">
<xsl:with-param name="toProcess" select="$leftToProcess"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template match="*" mode="getCols">
<xsl:value-of select="concat(local-name(),'~',@wd:type,'|')"/>
</xsl:template>
<xsl:template match="*" mode="header">
<xsl:value-of select="'|'"/>
<xsl:choose>
<xsl:when test="@wd:type">
<xsl:value-of select="concat(local-name(..),' ',@wd:type)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="local-name()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>

最新更新