我正在尝试自学XSL和XPATH。我有一个由我们的商业工具之一创建的示例XML文档,我想提取某些节点值并创建一个CSV文件作为输出。这里是一个截断的XML文档示例:
<?xml version="1.0" encoding="windows-1252"?>
<xml_report>
<form id= "WOI:WorkOrder" xmlns="http://www.w3.org/2000/xforms">
<mode l>
< group name="field-info" minOccurs="1" maxOccurs="1">
<group name="field" minOccurs="1" maxOccurs="*">
<string name="name" />
<number name="id" long="true" />
<string name="type" range="closed">
<value>CHAR</value>
<value>TIME</value>
<value>DECIMAL</value>
<value>REAL</value>
<value>INT</value>
<value>ENUM</value>
<value>ATTACH</value>
<value>DIARY</value>
<value>TIMEOFDAY</value>
<value>DATE</value>
<value>CURRENCY</value>
<value>NULL</value>
</string>
</group>
<!-- Additional group nodes -->
</group>
</model>
<instance>
<field-info>
<field>
<name>Work Order ID*+</name>
<id>1000000182</id>
<type> CHAR</type>
</field>
<!-- Additional field nodes -->
</field-info>
<entry>
<field_value>
<value>WO0000000498983</value>
</field_value>
<field_value>
<value>New Host name for new server build</value>
</field_value>
</entry>
<!-- Additional entry nodes -->
</instance>
</form>
</xml_report>
我想只提取值元素的内容,过滤掉其他所有内容。我已经编写了一些非常简单的XSL来尝试这样做:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" omit-xml-declaration="yes" indent="yes" encoding="utf-8" media-type="text/plain" />
<xsl:template match="/xml_report/form/instance">
<xsl:for-each select="entry/field_value">
<xsl:value-of select='value' /><xsl:text>,</xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
鉴于示例XML,我希望得到以下输出:
WO0000000498983,New Host name for new server build,
问题在于我实际上提取了我实际想要使用的节点列表之前的所有元素的值,以及不需要的缩进和行间距。我认为在模板匹配和for-each标签中指定限制性XPATH表达式就足够了,但事实并非如此。如何将所选节点的范围缩小到我实际想要使用的节点范围?如果有帮助的话,我在Windows 7上使用SAXON作为XSLT处理引擎。
CHAR
TIME
DECIMAL
REAL
INT
ENUM
ATTACH
DIARY
TIMEOFDAY
DATE
CURRENCY
NULL
Work Order ID*+
1000000182
CHAR
WO0000000498983
New Host name for new server build
答案 0 :(得分:1)
由于输入XML中的命名空间位于表单元素:
,因此无法获得所需的输出<form id="WOI:WorkOrder" xmlns="http://www.w3.org/2000/xforms">
因此,此表单中的所有元素都具有在XSLT中不匹配的此命名空间。 在XSLT
之后添加命名空间时,例如xmlns:xforms="http://www.w3.org/2000/xforms"
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xforms="http://www.w3.org/2000/xforms">
<xsl:output method="text" omit-xml-declaration="yes"
indent="yes" encoding="utf-8" media-type="text/plain" />
<xsl:template match="/xml_report">
<xsl:copy>
<xsl:apply-templates select="xforms:form"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/xml_report">
<xsl:apply-templates select="xforms:form/xforms:instance"/>
</xsl:template>
<xsl:template match="xforms:instance">
<xsl:for-each select="xforms:entry/xforms:field_value">
<xsl:value-of select='xforms:value' /><xsl:text>,</xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
在应用于示例XML时,在第4行中更正<model>
而不是<mode l>
,会产生以下输出:
WO0000000498983,New Host name for new server build,
为了避免误解 - 在这个XSLT中,我刚刚将xforms-namespace添加为xmlns:xforms
,没有必要这样命名。它会例如可以将其声明为xmlns:xfo="http://www.w3.org/2000/xforms"
,然后将<xsl:apply-templates select="xforms:form"/>
更改为<xsl:apply-templates select="xfo:form"/>
(并将其更改为当前前缀为xforms:
的其他元素)。
当您使用XSLT 2.0时,也可以将xforms
命名空间声明为xpath-default-namespace
,因为您只定位此命名空间中的元素。调整后的XSLT
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xpath-default-namespace="http://www.w3.org/2000/xforms">
<xsl:output method="text" omit-xml-declaration="yes"
indent="yes" encoding="utf-8" media-type="text/plain" />
<xsl:template match="//form">
<xsl:apply-templates select="instance"/>
</xsl:template>
<xsl:template match="instance">
<xsl:for-each select="entry/field_value">
<xsl:value-of select='value' /><xsl:text>,</xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
产生相同的输出。由于xforms
是default namespace
,因此无需添加额外的命名空间并为元素添加前缀。
此版本中的另一项调整是匹配form
而非xml_report
,因为xml_report
没有xforms
命名空间。
作为命名空间的参考,您可以例如查看http://www.w3.org/TR/REC-xml-names/#ns-decl
给出的What does "xmlns" in XML mean?或有价值的答案