XSLT转换XML生成RDF时XPATH失效,<exam:Cabecalho>节点为空求助
看起来你的XSLT代码里有几个关键的XPath和模板匹配问题,导致转换结果不符合预期,下面逐一拆解:
1. <exam:Cabecalho>节点为空的原因
这是模板匹配路径的核心问题!你的模板写的是:
<xsl:template match="documento/cabecalho"> <dc:title> <xsl:value-of select="titulo" /> </dc:title> </xsl:template>
而在根模板里调用<xsl:apply-templates select="documento/cabecalho" />时,你选中的是/documento/cabecalho节点。虽然match="documento/cabecalho"看起来是匹配这个节点,但某些XSLT处理器对这种相对路径的匹配模式会有歧义,更稳妥的方式是使用绝对路径明确指定要匹配的节点:
<xsl:template match="/documento/cabecalho"> <dc:title> <xsl:value-of select="titulo" /> </dc:title> </xsl:template>
或者简化成直接匹配cabecalho节点(如果你的XML里不会有其他同名节点):
<xsl:template match="cabecalho"> <dc:title> <xsl:value-of select="titulo" /> </dc:title> </xsl:template>
这样apply-templates就能正确找到模板并输出<dc:title>内容了。
2. rdf:about属性值为空的原因
你在属性值模板里用了{documento/cabecalho/titulo/text()},虽然在根节点上下文里这个路径逻辑上正确,但text()会返回文本节点集合,某些处理器在处理属性值时如果遇到空节点集会返回空字符串。更稳妥的写法是直接取元素的字符串值(不需要显式调用text()),同时使用绝对路径避免上下文混淆:
<rdf:Description rdf:about="{/documento/cabecalho/titulo}">
这样就能正确获取titulo的文本内容作为rdf:about的值了。
3. rdf:type错误指向documento的原因
这一点非常奇怪,因为你的原XSLT里明确写的是<rdf:type rdf:resource="Livro" />,但转换结果里变成了documento。可能的情况包括:
- 你误粘贴了错误的转换结果;
- 实际运行的XSLT文件被修改过;
- XSLT处理器出现了异常。
另外,注意Livro是相对URI,结合xml:base="http://example.com/",它的完整URI是http://example.com/Livro,如果你想更明确,可以写成<rdf:type rdf:resource="#Livro" />(如果类定义在同一个文档里)或者完整URI,但原写法本身是没问题的。
额外的RDF规范问题
你的XSLT里把Indice同时定义为类和属性:
<rdfs:Class rdf:about="Indice" /> <rdfs:Property rdf:about="Indice">
这会导致同一个URI(http://example.com/Indice)既是类又是属性,不符合RDF的语义规范,建议把属性名改成hasIndice或者其他区分开的名称,避免冲突。
修正后的XSLT大致如下:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE xsl:stylesheet [ <!ENTITY rdfs "http://www.w3.org/2000/01/rdf-schema#" > ]> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:foaf="http://xmlns.com/foaf/0.1/" xmlns:exam="http://example.com/"> <xsl:template match="/"> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:foaf="http://xmlns.com/foaf/0.1/" xml:base="http://example.com/" xmlns:exam="http://example.com/"> <rdfs:Class rdf:about="Livro"> <rdfs:subClassOf rdf:resource="Cabecalho" /> <rdfs:subClassOf rdf:resource="Parte" /> <rdfs:subClassOf rdf:resource="Seccao" /> <rdfs:subClassOf rdf:resource="Subseccao" /> <rdfs:subClassOf rdf:resource="Receita" /> </rdfs:Class> <rdfs:Class rdf:about="Cabecalho" /> <rdfs:Class rdf:about="Parte"> <rdfs:subClassOf rdf:resource="Indice" /> </rdfs:Class> <rdfs:Class rdf:about="Seccao"> <rdfs:subClassOf rdf:resource="Indice" /> <rdfs:subClassOf rdf:resource="Parte" /> </rdfs:Class> <rdfs:Class rdf:about="Subseccao"> <rdfs:subClassOf rdf:resource="Indice" /> <rdfs:subClassOf rdf:resource="Parte" /> <rdfs:subClassOf rdf:resource="Seccao" /> </rdfs:Class> <rdfs:Class rdf:about="Receita"> <rdfs:subClassOf rdf:resource="Indice" /> <rdfs:subClassOf rdf:resource="Parte" /> <rdfs:subClassOf rdf:resource="Seccao" /> <rdfs:subClassOf rdf:resource="Subseccao" /> </rdfs:Class> <rdfs:Property rdf:about="nome"> <rdfs:domain rdf:resource="Livro" /> <rdfs:range rdf:resource="&rdfs;Literal" /> </rdfs:Property> <rdfs:Property rdf:about="hasIndice"> <rdfs:domain rdf:resource="Livro" /> <rdfs:range rdf:resource="Livro" /> </rdfs:Property> <rdf:Description rdf:about="{/documento/cabecalho/titulo}"> <rdf:type rdf:resource="Livro" /> <exam:Cabecalho> <xsl:apply-templates select="/documento/cabecalho" /> </exam:Cabecalho> </rdf:Description> </rdf:RDF> </xsl:template> <xsl:template match="/documento/cabecalho"> <dc:title> <xsl:value-of select="titulo" /> </dc:title> </xsl:template> </xsl:stylesheet>
内容的提问来源于stack exchange,提问作者Rui Pedro

