[lxml-dev] Using the Xpath id function
Hi I've sent this to comp.lang.python a few days ago with no repsonse, so I hope you don't mind if I try here (if there is a better place please let me know)... Basically I'm trying to use the .xpath('id("foo")') method on an lxml tree but can't get it to work. Given the following XML: <root><child id="foo"/></root> And it's XMLSchema: <?xml version="1.0" encoding="UTF-8"?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"> <xs:element name="root"> <xs:complexType> <xs:sequence> <xs:element ref="child"/> </xs:sequence> </xs:complexType> </xs:element> <xs:element name="child"> <xs:complexType> <xs:attribute name="id" use="required" type="xs:ID"/> </xs:complexType> </xs:element> </xs:schema> Or in more readable, compact RelaxNG, form: element root { element child { attribute id { xsd:ID } } } Now I'm trying to parse the XML and use the .xpath() method to find the <child/> element using the id XPath function: from lxml import etree schema_root = etree.parse(file('schema.xsd')) schema = etree.XMLSchema(schema_root) parser = etree.XMLParser(schema=schema) root = etree.fromstring('<root><child id="foo"/></root>', parser) root.xpath('id("foo")') --> [] I was expecting to get the <child/> element with that last statement (well, inside a list that is), but instead I just get an empty list. Is there anything obvious I'm doing wrong? As far as I can see the lxml documentation says this should work. Cheers Floris For completeness, `dpkg -l python-lxml' tells me I'm using Debian lenny's lxml 2.0.6-1 -- Debian GNU/Linux -- The Power of Freedom www.debian.org | www.gnu.org | www.kernel.org
Hi,
from lxml import etree schema_root = etree.parse(file('schema.xsd')) schema = etree.XMLSchema(schema_root) parser = etree.XMLParser(schema=schema) root = etree.fromstring('<root><child id="foo"/></root>', parser) root.xpath('id("foo")') --> []
I was expecting to get the <child/> element with that last statement (well, inside a list that is), but instead I just get an empty list. Is there anything obvious I'm doing wrong? As far as I can see the lxml documentation says this should work.
You can always check for the id attribute without any schema involvement: >>> from lxml import etree
root = etree.fromstring('<root><child id="foo"/></root>')
root.xpath('//*[@id="foo"]') ['']
for elt in root.xpath('//*[@id="foo"]'): print etree.tostring(elt) ... <child id="foo"/>
Regarding the xpath id() function I think you'd need a DTD:
See http://www.w3.org/TR/xpath: """ Function: node-set id(object) The id function selects elements by theirunique ID (see [5.2.1 Unique IDs]). """ and: """ 5.2.1 Unique IDs An element node may have a unique identifier (ID). This is the value of the attribute that is declared in the DTD as type ID. No two elements in a document may have the sameunique ID. If an XML processor reports two elements in a document ashaving the same unique ID (which is possible only if the document isinvalid) then the second element in document order must be treated asnot having a unique ID.
NOTE: If a document does not have a DTD, then no element in thedocument will have a unique ID.
"""
Holger
-- Psssst! Schon vom neuen GMX MultiMessenger gehört? Der kann`s mit allen: http://www.gmx.net/de/go/multimessenger
jholg@gmx.de wrote:
Hi,
from lxml import etree schema_root = etree.parse(file('schema.xsd')) schema = etree.XMLSchema(schema_root) parser = etree.XMLParser(schema=schema) root = etree.fromstring('<root><child id="foo"/></root>', parser) root.xpath('id("foo")') --> []
I was expecting to get the <child/> element with that last statement (well, inside a list that is), but instead I just get an empty list. Is there anything obvious I'm doing wrong? As far as I can see the lxml documentation says this should work.
[...]
You can always check for the id attribute without any schema involvement:
[...]
Regarding the xpath id() function I think you'd need a DTD:
[...] xml:id, http://www.w3.org/TR/xml-id/ works OK, if you do not want to use a schema. At least, it works with lxml 2.1.beta3. from lxml import etree XML_NAMESPACE='http://www.w3.org/XML/1998/namespace' XML_PREFIX= '{%s}' % XML_NAMESPACE f = etree.Element('test') f.set(XML_PREFIX+'id','23455') etree.tostring(f) '<test xml:id="23455"/>' g = etree.SubElement(f,'test1') g.set(XML_PREFIX+'id','23456') f.xpath('id("23456")') [<Element test1 at 777520>] f.xpath('id("23455")') [<Element test at 7774c8>]
- Jim Washington
Hello 2008/7/2 Jim Washington <jwashin@vt.edu>:
jholg@gmx.de wrote:
from lxml import etree schema_root = etree.parse(file('schema.xsd')) schema = etree.XMLSchema(schema_root) parser = etree.XMLParser(schema=schema) root = etree.fromstring('<root><child id="foo"/></root>', parser) root.xpath('id("foo")') --> []
I was expecting to get the <child/> element with that last statement (well, inside a list that is), but instead I just get an empty list. Is there anything obvious I'm doing wrong? As far as I can see the lxml documentation says this should work.
[...]
You can always check for the id attribute without any schema involvement:
[...]
Regarding the xpath id() function I think you'd need a DTD:
Indeed, converting the schema to a DTD solved my problem! Too simple to think of myself of course. Somehow the documentation must have confused me into believing validation with XMLSchema or DTD would work (I did understand RelaxNG wouldn't work). Converting the schema is simple using trang anyway (which I need to do in any case as I'm writing it in compact RelaxNG).
xml:id, http://www.w3.org/TR/xml-id/ works OK, if you do not want to use a schema. At least, it works with lxml 2.1.beta3.
from lxml import etree XML_NAMESPACE='http://www.w3.org/XML/1998/namespace' XML_PREFIX= '{%s}' % XML_NAMESPACE f = etree.Element('test') f.set(XML_PREFIX+'id','23455') etree.tostring(f) '<test xml:id="23455"/>' g = etree.SubElement(f,'test1') g.set(XML_PREFIX+'id','23456') f.xpath('id("23456")') [<Element test1 at 777520>] f.xpath('id("23455")') [<Element test at 7774c8>]
That's quite nifty too! Don't think I'll use it this time but very good to know. Thanks for your tips Floris -- Debian GNU/Linux -- The Power of Freedom www.debian.org | www.gnu.org | www.kernel.org
Hi,
Indeed, converting the schema to a DTD solved my problem! Too simple to think of myself of course. Somehow the documentation must have confused me into believing validation with XMLSchema or DTD would work (I did understand RelaxNG wouldn't work). Converting the schema is simple using trang anyway (which I need to do in any case as I'm writing it in compact RelaxNG).
Just to clarify: lxml *does* validate (if told so), both against a W3C XMLSchema and a RelaxNG schema. So from my point of view, using a schema rather than a DTD nowadays is usually the right choice, if only because the schema is itself well-formed XML - if possible. How this relates to the XPath id() function and the concept of the ID attribute in general I don't really know, from what I've looked up in the spec this concept seems somehow tied to DTDs (?) Holger -- Ist Ihr Browser Vista-kompatibel? Jetzt die neuesten Browser-Versionen downloaden: http://www.gmx.net/de/go/browser
Jim Washington wrote:
xml:id, http://www.w3.org/TR/xml-id/ works OK, if you do not want to use a schema. At least, it works with lxml 2.1.beta3.
from lxml import etree XML_NAMESPACE='http://www.w3.org/XML/1998/namespace' XML_PREFIX= '{%s}' % XML_NAMESPACE f = etree.Element('test') f.set(XML_PREFIX+'id','23455') etree.tostring(f) '<test xml:id="23455"/>' g = etree.SubElement(f,'test1') g.set(XML_PREFIX+'id','23456') f.xpath('id("23456")') [<Element test1 at 777520>] f.xpath('id("23455")') [<Element test at 7774c8>]
A quick blunder fix: The above works, but is not quite in accordance with the xml:id specification. Other XML processors may balk at the above. The error in this case is that the value of xml:id attributes cannot start with digits. The concept works, but it is a bad example. - Jim Washington
participants (3)
-
Floris Bruynooghe -
jholg@gmx.de -
Jim Washington