我是XSLT的新手。我需要通过pdf2txt.py聚合xml格式的PDF文件内容的一些信息。有些PDF文件很大(超过100MB),甚至更大的是它们的xml输出。因此,通过几个xsltproc命令处理所有内存管道输出,以便从不需要的内容中删除xml代码,似乎更有效(节省时间)。除其他事项外,还有一个xml节点,其中包含文本内容,我想将其转换为其父节点的属性。
更具体地说,我有以下输入XML文件结构:<?xml version="1.0"?>
<pages>
<page id="1">
<text bbox="2831.881,1170.243,3124.184,1192.535">text11</text>
<text bbox="3149.641,1291.323,3318.336,1313.615">sheet</text>
<text bbox="3149.641,1291.323,3318.336,1313.615">P793</text>
</page>
<page id="2">
<text bbox="2831.881,1170.243,3124.184,1192.535">text21</text>
<text bbox="3149.641,1291.323,3318.336,1313.615">sheet:</text>
<text bbox="3149.641,1291.323,3318.336,1313.615">S234</text>
</page>
</pages>
,我想把转换成(注意添加的page属性):
<?xml version="1.0"?>
<pages>
<page id="1" sheet="P793">
<text bbox="2831.881,1170.243,3124.184,1192.535">text11</text>
<text bbox="3149.641,1291.323,3318.336,1313.615">sheet</text>
<text bbox="3149.641,1291.323,3318.336,1313.615">P793</text>
</page>
<page id="2" sheet="S234">
<text bbox="2831.881,1170.243,3124.184,1192.535">text21</text>
<text bbox="3149.641,1291.323,3318.336,1313.615">sheet</text>
<text bbox="3149.641,1291.323,3318.336,1313.615">S234</text>
</page>
</pages>
按照XSLT中的示例:根据包含特定字符串的子属性值向父属性添加属性,我尝试使用以下XSL样式表:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="no" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:preserve-space elements="text"/>
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="page">
<xsl:apply-templates select="@*"/>
<xsl:variable name="sheet" select="//text[contains(text(),'sheet')]/following::text[string-length()>3]"/>
<xsl:attribute name="sheet"><xsl:copy-of select="$sheet" /></xsl:attribute>
<xsl:apply-templates select="node()"/>
</xsl:template>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
但是,我没有得到输出。为了定义新的页面属性,我尝试用文本节点上的for-each循环替换变量技巧,但随后我得到了错误,我试图在添加子节点后添加属性,这是我不太理解的。
是否可以"提前查找"这样的节点值并使用它向父节点添加属性?如何?为什么我的样式表没有任何输出?
我的最终目标是删除与表节点及其标签相对应的XML文本行,但这似乎比前面的属性复制更容易解决,我将在后面处理它。
谢谢!
编辑:我简化了输入大小写和xsl样式表。实际上,在这里提供的示例中有一个输出,但它是一个错误输出:
runtime error: file test.xsl line 18 element copy
Attribute nodes must be added before any child nodes to an element.
runtime error: file test.xsl line 13 element attribute
xsl:attribute: Cannot add attributes to an element if children have been already added to the element.
no result for -
这是一个错误,我还没有弄清楚如何处理。
主要问题是在匹配page
的模板中,您要做的第一件事是创建一个属性
<xsl:template match="page">
<xsl:apply-templates select="@*"/>
但是您实际上没有首先复制page
元素,因此它将尝试将属性和子text
节点添加到先前创建的元素上;即pages
。对于第二个匹配的page
元素,它将尝试做同样的事情,但会出错,因为您不能向已经添加了子元素的元素添加属性。
试试这个模板
<xsl:template match="page">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:variable name="sheet" select="text[contains(text(),'sheet')]/following-sibling::text[string-length()>3]"/>
<xsl:attribute name="sheet"><xsl:value-of select="$sheet" /></xsl:attribute>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:template>
注意sheet
表达式的变化。以前,您从//text
开始,它将在文档的任何地方找到第一个text
元素。需要删除//
,使其相对于当前page
节点。
另外,注意使用following-sibling
,而不是following
,这样它将自己限制为当前page
元素下的兄弟节点。
最后,是否只希望访问紧跟其后的兄弟节点?如果是这样,您可能需要向表达式
添加一个额外的条件。<xsl:variable name="sheet" select="text[contains(text(),'sheet')]/following-sibling::text[1][string-length()>3]"/>
或者反过来写
<xsl:variable name="sheet" select="text[string-length()>3][contains(preceding-sibling::text[1],'sheet')]"/>