使用python的lxml库在xslt中获取父元素

时间:2013-05-30 05:12:09

标签: python xslt lxml

我是lxml和xslt的新手并尝试执行以下操作 - 这是我的xml:

<root>
<header>
    <h1>foo</h1>
</header>
<product>
    <a>something1</a>
    <b>something2</b>
</product>
<product>
    <a>something3</a>
    <b>something4</b>
</product>
</root>

使用lxml XSLT类,我想查看每个产品元素,然后对其应用一些规则,例如:

def example():
example_xml = '''\
<root>
    <header><h1>foo</h1></header>
    <product><a>something1</a><b>something2</b></product>
    <product><a>something3</a><b>something4</b></product>
</root>'''
xslt_xml = etree.XML('''\
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions">
        <xsl:template match="/">
            <foo><xsl:value-of select="a" /></foo>
        </xsl:template>
    </xsl:stylesheet>
    ''')
transform = etree.XSLT(xslt_xml)
doc = etree.parse(StringIO(example_xml))
root = doc.getroot()
for product in root.iterfind('product'):
    result = transform(product)
    print result

这个例子找不到,只有当我将xslt_xml中的“match”属性更改为match =“product”时才有效。我认为匹配=“/”表示匹配根,所以我不确定为什么这不起作用。

我更大的问题是我还希望获得有关祖先或产品父母的元素的信息,但这不起作用,即此xslt不会返回任何内容:

    xslt_xml = etree.XML('''\
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions">
        <xsl:template match="product">
            <foo><xsl:value-of select="../header/h1" /></foo>
        </xsl:template>
    </xsl:stylesheet>
    ''')

所以在示例代码中:

for product in root.iterfind('product'):
    print product.xpath('../header/h1/text()') #works
    result = transform(product)
    print result #doesn't work

如果我在产品上运行xslt转换,是否可以访问这些元素?我不想在整个文档上运行xslt,我想查看每个产品元素并处理它,我知道我可以使用xslt完成'for each product'循环,但我不希望这样,这个循环在xslt处理的外部。

任何帮助将不胜感激,谢谢!

1 个答案:

答案 0 :(得分:1)

你的第一个例子不起作用的原因似乎是对root的一点误解。 例如。 *root node*enter link description here
树的顶部是根节点(1.0术语)或文档节点(2.0)。这就是“/”所指的。它不是一个元素:它是最外层元素的父元素(以及在最外层元素之前或之后的任何注释和处理指令)。根节点没有名称。

根节点的子节点没有“a”元素。根节点的子节点是您的根元素。

下一点你必须考虑艺术 Built-in Template Rules。 这就是为什么你的第二个例子

                    似乎工作。但输出应该是:

             foo

   <foo>something1</foo>
   <foo>something3</foo>

但我不清楚你的第三个结果: 使用此模板:

<xsl:template match="product">
    <foo>
        <xsl:value-of select="../header/h1" />
    </foo>
</xsl:template>

输出应该/(将):

            foo

    <foo>foo</foo>
    <foo>foo</foo>

避免第一个孤独的“foo”。您必须避免使用build-i模板规则。 例如,尝试:

<xsl:template match="/*" >
    <xsl:apply-templates select="product" />
</xsl:template>
<xsl:template match="product">
    <foo>
        <xsl:value-of select="../header/h1" />
    </foo>
</xsl:template>

更新:
上面的语句只有在为xml文档调用transform时才是正确的。 但是对于产品节点(for product in root.iterfind('product'):)调用transform()。 从xslt的角度来看,此产品节点现在是文档。 (没有上下文节点表单样式表的观点)因此无法访问任何父节点或兄弟节点。

如果您想使用xslt,最好使用xml文档的传输调用。 然后迭代结果(如果需要)。

此样式表:

xslt_xml = etree.XML('''\
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     <xsl:template match="/" >
     <root>
        <xsl:apply-templates select="//product" />
     </root>
    </xsl:template>
    <xsl:template match="product">
            foo><xsl:value-of select="../header/h1/text()" />,<xsl:value-of select="a" /></foo>
        </xsl:template>
    </xsl:stylesheet>
    ''')

您可以遍历新的foo节点。

transform = etree.XSLT(xslt_xml)
doc = etree.parse(StringIO(example_xml))
root = doc.getroot()
result = transform(root)
# print (str(result ))

for foo in result.iterfind('foo'):
    print (foo.text)

输出:

foo,something1
foo,something3