XSL处理XML:保留首个div1并继承type属性报错求助
问题解决:修复XSL中div1属性设置的错误
问题背景
处理包含大量空div1元素的TEI XML集合,需实现两个需求:
- 移除除首个
div1外的所有div1元素 - 将
<text>元素的type属性值填充到首个<div1>的type属性中
源XML示例片段:
<text type="letter" xmlns="http://www.tei-c.org/ns/1.0"> <body> <pb n="1" facs="stefansson-wrangel-09-21-010-001"/> <div1 type=""> <p corresp="TTP32407168P0"> <add> <hi rend="underline">Noice</hi> </add> </p> <p corresp="TTP32407168P1"> <date when="1923-02-02">February 2, 1923</date> </p> <p corresp="TTP32407168P2">Dear Mr. <persName key="S32010887">Stefansson</persName>: </p> <p corresp="TTP32407168P3"> <persName key="S32010970">Joe Bernard</persName> left Monday. His present intention<lb/> is to go to Boston, then on to Montreal, where he will spend the greater<lb/> part of the winter. <persName key="S32010970">Joe</persName> plans to try to get museums, sportsmen, or anyone<lb/> else to finance a trip to the Arctic, (preferably <placeName key="S32010957">Coronation Gulf</placeName>), although<lb/> if he hears from you in the meantime he will go to <placeName key="S32010932">Wrangel</placeName>. </p> ... </div1>
原XSL代码已实现移除多余div1,但设置属性时报错:
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xpath-default-namespace="http://www.tei-c.org/ns/1.0" xmlns="http://www.tei-c.org/ns/1.0" version="3.0"> <xsl:output method="xml" indent="yes"/> <xsl:variable name="genre" select="text/@type"></xsl:variable> <xsl:template match="node()|@*"> <xsl:copy> <xsl:apply-templates select="node()|@*"/> </xsl:copy> </xsl:template> <xsl:template match="div1[position()!=1]"> <xsl:apply-templates select="node()"/> </xsl:template> <xsl:template match="div1[position()=1]"> <xsl:attribute name="type"> <xsl:value-of select="$genre"/> </xsl:attribute> </xsl:template> </xsl:stylesheet>
错误提示:An attribute node (type) cannot be created after a child of the containing element. Most recent element start tag was output at line 12 of module remove-extra-divs.xsl
错误原因
原代码中匹配首个div1的模板直接输出type属性,但没有先复制div1元素本身,违反了XSLT的规则:属性节点必须紧跟在所属元素的开始标签之后,不能在子节点之后创建。同时该模板还会丢失div1的所有子节点。
修正后的XSL代码
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xpath-default-namespace="http://www.tei-c.org/ns/1.0" xmlns="http://www.tei-c.org/ns/1.0" version="3.0"> <xsl:output method="xml" indent="yes"/> <!-- 正确获取根text元素的type属性 --> <xsl:variable name="genre" select="/text/@type"/> <!-- 身份模板:默认复制所有节点和属性 --> <xsl:template match="node()|@*"> <xsl:copy> <xsl:apply-templates select="node()|@*"/> </xsl:copy> </xsl:template> <!-- 移除除首个div1外的所有div1标签,保留其内容 --> <xsl:template match="div1[position()!=1]"> <xsl:apply-templates select="node()"/> </xsl:template> <!-- 替换首个div1的type属性值 --> <xsl:template match="div1[position()=1]/@type"> <xsl:attribute name="type"> <xsl:value-of select="$genre"/> </xsl:attribute> </xsl:template> </xsl:stylesheet>
关键改动说明
- 修正属性设置逻辑:不再直接输出属性,而是匹配首个
div1的type属性,替换其值。这样身份模板会先复制div1元素,再处理属性,符合XSLT的节点输出顺序规则。 - 确保变量路径准确:将
$genre的选择路径明确为/text/@type,确保从根节点的text元素获取type属性值(避免上下文导致的取值错误)。 - 保留div1的子节点:通过身份模板和属性替换模板的配合,既修改了
type属性,又完整保留了div1的所有内容。
内容的提问来源于stack exchange,提问作者Liz
相关产品推荐
相关产品推荐

