替代标题:如何将 CloudMade API 响应集转换为单个 CSV 文件?
我有大约 1000 个 XML 文件,其中包含来自CloudMade API的地理编码响应。
据我所知,CloudMade 没有批处理 API,也不输出 CSV。
我想将一组 XML 文件转换为一个 CSV 文件,其中每个响应包含一行。
是否可以仅使用 XSLT 1.0 来做到这一点?如果没有,是否存在 XSLT 2.0 解决方案?
CSV 必须至少包含三列:ID、纬度和经度。
每个 XML 文件的基本名称都包含响应 ID。
第一个 Array 元素的 Latitude 和 Longitude 元素包含纬度和经度值。
小例子
这是一个只有两个 XML 文件的小示例。
文件140.xml
如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<places>
<Array pos="0">
<addressType>housenumber</addressType>
<city>~Weiz</city>
<country>Austria</country>
<featureType>Ortsstrasse</featureType>
<houseNumber>19</houseNumber>
<position>
<lat>47.22148736</lat>
<lon>15.62440613</lon>
</position>
<street>Dr.-Karl-Widdmann-Straße</street>
<zip>8160</zip>
</Array>
</places>
<status>
<duration>205</duration>
<procedure>geo.location.search.2</procedure>
<success>true</success>
</status>
</Response>
文件141.xml
如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<places>
<Array pos="0">
<addressType>housenumber</addressType>
<city>~Innsbruck</city>
<country>Austria</country>
<featureType>Ortsstrasse</featureType>
<houseNumber>50</houseNumber>
<position>
<lat>47.26638083</lat>
<lon>11.43725792</lon>
</position>
<street>Valiergasse</street>
<zip>6020</zip>
</Array>
</places>
<status>
<duration>139</duration>
<procedure>geo.location.search.2</procedure>
<success>true</success>
</status>
</Response>
输出cloudmade_responses.csv
应以 UTF-8 编码,应如下所示:
"Id","Latitude","Longitude"
"140","47.22148736","15.62440613"
"141","47.26638083","11.43725792"
部分 XSLT 解决方案
我对基本的 XPath 很满意,但不确定如何将 XPath 表达式集成到更复杂的 XSLT 文档中。
提取纬度的 XPath 表达式是
/Response/places/Array[@pos=0]/position/lat
提取经度的 XPath 表达式是
/Response/places/Array[@pos=0]/position/lon
将这些传递给XmlStar以将单个文档转换为不带引号的 CSV 行:
$ xml sel -t -v "/Response/places/Array[@pos=0]/position/lat" -o "," -v "/Response/places/Array[@pos=0]/position/lon" 140.xml
47.22148736,15.62440613
添加-C
选项并对输出进行管道传输会写入转换的 XSLT 描述:
xml select -C -t -v "/Response/places/Array[@pos=0]/position/lat" -o "," -v "/Response/places/Array[@pos=0]/position/lon" 140.xml > partial_solution.xslt
输出partial_solution.xslt
如下所示:
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exslt="http://exslt.org/common" version="1.0" extension-element-prefixes="exslt">
<xsl:output omit-xml-declaration="yes" indent="no"/>
<xsl:template match="/">
<xsl:call-template name="value-of-template">
<xsl:with-param name="select" select="/Response/places/Array[@pos=0]/position/lat"/>
</xsl:call-template>
<xsl:text>,</xsl:text>
<xsl:call-template name="value-of-template">
<xsl:with-param name="select" select="/Response/places/Array[@pos=0]/position/lon"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="value-of-template">
<xsl:param name="select"/>
<xsl:value-of select="$select"/>
<xsl:for-each select="exslt:node-set($select)[position()>1]">
<xsl:value-of select="' '"/>
<xsl:value-of select="."/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
我现在可以使用 XSLT 文件执行相同的转换:
$ xml tr partial_solution.xslt 140.xml
47.22148736,15.62440613
但是,我不确定如何修改 XSLT 描述以满足我的所有要求。
老实说,我也不能说我完全理解部分 XSLT 解决方案。
使用脚本语言的完整解决方案
PowerShell 是一种脚本语言,内置支持 XML 和 CSV 处理。凭借其简洁的管道语法,您可以用几行代码解决问题:
Get-ChildItem -Path |
Select -Property @(
@{ Name = 'Id'; Expression = { $_.BaseName } },
@{ Name = 'Latitude'; Expression = {(Select-Xml -Path $_.FullName -XPath '/Response/places/Array[@pos=0]/position/lat').Node.InnerText } },
@{ Name = 'Longitude'; Expression = {(Select-Xml -Path $_.FullName -XPath '/Response/places/Array[@pos=0]/position/lon').Node.InnerText } }
) |
Export-Csv -Path '.\cloudmade_responses.csv' -NoTypeInformation -Encoding UTF8
在与 XML 文件相同的目录中执行该操作会生成一个名为cloudmade_response.csv
. 它看起来像这样:
"Id","Latitude","Longitude"
"140","47.22148736","15.62440613"
"141","47.26638083","11.43725792"
输出与指定的完全相同。
在 Python 和 Perl 等其他脚本语言中肯定也有类似的简洁解决方案。
使用 XSLT 解决问题应该允许任何具有 XSLT 处理器的语言重用该解决方案。