Saxon免费解析器中如何正确提取指定节点的属性值?
问题:使用Saxon免费解析器处理XML时,如何仅返回属性值?
我正在使用Saxon免费解析器处理XML文件,XML片段如下:
<osmChange> <modify> <node id="28377390"> <tag k="name" v="Holywell"/> <tag k="public_transport" v="station"/> <tag k="railway" v="station"/> </node> <node id="168325631"> <tag k="name" v="Whitecraigs"/> <tag k="public_transport" v="station"/> <tag k="railway" v="station"/> </node> </modify> </osmChange>
使用以下XPath可以选中目标<tag>节点:
<xsl:sequence select="$OSM_OSC/osmChange/modify/(node|way)[tag[@k='railway'][@v='station']]/tag[@k='name']"/>
得到结果:
<tag k="name" v="Whitecraigs"/> <tag k="name" v="Holywell"/>
我希望仅返回属性v的值,即:
Whitecraigs Holywell
尝试在XPath末尾添加/@v,修改后的代码如下:
<xsl:sequence select="$OSM_OSC/osmChange/modify/(node|way)[tag[@k='railway'][@v='station']]/tag[@k='name']/@v"/>
但出现错误:
java.lang.UnsupportedOperationException: copy() applied to attribute node at net.sf.saxon.tree.tiny.TinyAttributeImpl.copy(TinyAttributeImpl.java:259) at net.sf.saxon.serialize.UncommittedSerializer.append(UncommittedSerializer.java:207) at net.sf.saxon.event.ProxyReceiver.append(ProxyReceiver.java:200) at net.sf.saxon.serialize.PrincipalOutputGatekeeper.append(PrincipalOutputGatekeeper.java:101)
请问如何正确实现仅返回属性值的需求?
解决方案
这个错误的核心原因是:<xsl:sequence>尝试直接输出属性节点,但XML属性无法脱离父元素单独存在,序列化器在复制属性节点时会触发异常。要解决问题,需要提取属性的字符串值,而非输出属性节点本身。
以下是两种可靠的实现方式:
1. 使用string()函数提取属性值
修改XPath,用string()包裹属性节点,直接获取其字符串值:
<xsl:sequence select="string($OSM_OSC/osmChange/modify/(node|way)[tag[@k='railway'][@v='station']]/tag[@k='name']/@v)"/>
如果需要匹配多个节点并逐个输出值,可配合for-each循环:
<xsl:for-each select="$OSM_OSC/osmChange/modify/(node|way)[tag[@k='railway'][@v='station']]/tag[@k='name']/@v"> <xsl:sequence select="string(.)"/> </xsl:for-each>
2. 使用<xsl:value-of>代替<xsl:sequence>
<xsl:value-of>会自动提取节点的字符串值,适合直接输出文本内容,还能通过separator指定值之间的分隔符:
<xsl:value-of select="$OSM_OSC/osmChange/modify/(node|way)[tag[@k='railway'][@v='station']]/tag[@k='name']/@v" separator=" "/>
其中separator=" "用来设置换行符,确保输出结果为每行一个属性值,完全符合预期格式。
补充:XPath 2.0+可用data()函数
如果使用的是XPath 2.0及以上版本,也可以用data()函数,它专门用于提取原子值,效果与string()类似:
<xsl:sequence select="data($OSM_OSC/osmChange/modify/(node|way)[tag[@k='railway'][@v='station']]/tag[@k='name']/@v)"/>
内容的提问来源于stack exchange,提问作者DaveF
相关产品推荐
相关产品推荐

