我正在尝试将XML解析为平面文件。在我在SO上找到的与此主题相关的许多主题中,这两个都是我希望完成的部分内容。
XML to CSV using XSLT help
XML to CSV using XSLT
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 & 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
这是一个很好的解决方案,因为它可以深入到每个子节点并放入分隔符,但不了解先前祖先的子节点。另外,我不希望页面/结果/总计页面信息出现。我添加了标准模板替代,但没有做任何事情。
<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
。
第二步是创建一个变量(|
),该变量包含由getCols
分隔的所有键(local-name()/ wd:type combos)的唯一列表。这个变量将允许我递归处理它,以确保每一行都有每一列的条目,并且顺序将始终相同。该值是通过仅使用模式模板(模式allCols
)处理每个键(Muenchian分组)中的第一个节点而创建的。
由于第一行(标题)中的列条目需要与header
中的值不同,因此我创建了另一个名为allCols
的变量。创建此变量的值的方式类似于wd:type
,但是它仅使用本地名称或父级本地名称以及header
属性的值(取决于该属性是否存在)。它还使用模式为substring($temp,2)
的模式化模板。您会注意到我在此变量中使用了|
。这是为了从$temp
的开头删除不需要的wd:School_Data
。
为了将处理仅限于/*
,我匹配了根元素(wd:School_Data
),并且仅在xsl:for-each
中选择了header
。我还输出wd:School_Data
变量的值。
处理outputFields
元素时,我将命名模板称为allCols
。我传递了一个包含outputFields
变量的参数。field
模板是大多数处理的地方。首先,创建四个变量。
前两个变量leftToProcess
和elemName
是第一个字段和其余字段。
后两个变量elemType
和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,'
')"/>
<xsl:for-each select="//wd:School_Data">
<xsl:call-template name="outputFields">
<xsl:with-param name="toProcess" select="$allCols"/>
</xsl:call-template>
<xsl:text>
</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>
关于xml - XSLT-使用XSLT 1的XML到CSV动态模板,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/48412333/