19

この厄介な問題は2回目だったので、聞いてみるといいと思いました。

XMLドキュメントからElementsを取得する必要がある場合もありますが、これを行う方法は厄介です。

私が望むことを実行するPythonライブラリ、XPathを定式化するエレガントな方法、名前空間をプレフィックスに自動的に登録する方法、または組み込みのXML実装またはlxmlに隠された設定を登録して、名前空間を完全に削除する方法を知りたいです。あなたが私が欲しいものをすでに知っていない限り、明確化が続きます:)

例-ドキュメント:

<root xmlns="http://really-long-namespace.uri"
  xmlns:other="http://with-ambivalent.end/#">
    <other:elem/>
</root>

私は何ができますか

ElementTree APIは、XPathクエリを提供する(私が知っている)唯一の組み込みAPIです。ただし、「UNames」を使用する必要があります。これは次のようになります。/{http://really-long-namespace.uri}root/{http://with-ambivalent.end/#}elem

ご覧のとおり、これらは非常に冗長です。次のようにすることで、それらを短縮できます。

default_ns = "http://really-long-namespace.uri"
other_ns   = "http://with-ambivalent.end/#"
doc.find("/{{{0}}}root/{{{1}}}elem".format(default_ns, other_ns))

しかし、これは{{{ugly}}}であり、≃≃≃なので壊れやすいものhttp…end/#ですhttp…end#。どのバリアントが使用されるかを誰が知っていますか?http…end/http…end

また、lxmlは名前空間プレフィックスをサポートしますが、ドキュメント内のプレフィックスを使用せず、デフォルトの名前空間を処理する自動化された方法も提供しません。ドキュメントから取得するには、各名前空間の1つの要素を取得する必要があります。名前空間属性は保持されないため、これらから自動的に取得する方法もありません。

XPathクエリには名前空間に依存しない方法もありますが、冗長で醜く、組み込みの実装では使用できません。/*[local-name() = 'root']/*[local-name() = 'elem']

私がやりたいこと

以下を少し入力するだけで、上記の例を実現するためのライブラリ、オプション、または汎用XPathモーフィング関数を見つけたいと思います…</ p>

  1. 名前なし:/root/elem
  2. 名前空間-ドキュメントのプレフィックス:/root/other:elem

…さらに、ドキュメントのプレフィックスを使用したり、名前空間を削除したりするステートメントもあります。

さらに明確にする:私の現在のユースケースはそれと同じくらい単純ですが、将来はもっと複雑なものを使用する必要があります。

読んでくれてありがとう!


解決しました

ユーザーsamplebiasは、私の注意をpy-dom-xpathに向けました。まさに私が探していたもの。私の実際のコードは次のようになります。

#parse the document into a DOM tree
rdf_tree = xml.dom.minidom.parse("install.rdf")
#read the default namespace and prefix from the root node
context = xpath.XPathContext(rdf_tree)

name    = context.findvalue("//em:id", rdf_tree)
version = context.findvalue("//em:version", rdf_tree)

#<Description/> inherits the default RDF namespace
resource_nodes = context.find("//Description/following-sibling::*", rdf_tree)

ドキュメントと一貫性があり、シンプルで名前空間を認識します。完全。

4

2 に答える 2

14

構文は機能する*[local-name() = "elem"]はずですが、より簡単にするために、部分的または完全な「ワイルドカード名前空間」XPath 式の構築を簡素化する関数を作成できます。

Ubuntu 10.04 で python-lxml 2.2.4を使用していますが、以下のスクリプトが機能します。各要素のデフォルトの名前空間を指定する方法に応じて、動作をカスタマイズする必要があります。さらに、式に折りたたむその他の XPath 構文を処理する必要があります。

import lxml.etree

def xpath_ns(tree, expr):
    "Parse a simple expression and prepend namespace wildcards where unspecified."
    qual = lambda n: n if not n or ':' in n else '*[local-name() = "%s"]' % n
    expr = '/'.join(qual(n) for n in expr.split('/'))
    nsmap = dict((k, v) for k, v in tree.nsmap.items() if k)
    return tree.xpath(expr, namespaces=nsmap)

doc = '''<root xmlns="http://really-long-namespace.uri"
    xmlns:other="http://with-ambivalent.end/#">
    <other:elem/>
</root>'''

tree = lxml.etree.fromstring(doc)
print xpath_ns(tree, '/root')
print xpath_ns(tree, '/root/elem')
print xpath_ns(tree, '/root/other:elem')

出力:

[<Element {http://really-long-namespace.uri}root at 23099f0>]
[<Element {http://with-ambivalent.end/#}elem at 2309a48>]
[<Element {http://with-ambivalent.end/#}elem at 2309a48>]

更新: XPath を解析する必要があることがわかった場合は、(ほとんどの) XPath 1.0 の純粋な Python 実装であるpy-dom-xpathなどのプロジェクトをチェックアウトできます。少なくとも、XPath の構文解析の複雑さについてある程度理解できるでしょう。

于 2011-04-06T21:36:23.090 に答える
2

まずは「やりたいこと」について。

  1. Unnamespaced: /root/elem-> ここでは問題ないと思います
  2. ドキュメントからの名前空間プレフィックス: /root/other:elem-> まあ、それは少し問題です。「ドキュメントからの名前空間プレフィックス」だけを使用することはできません。1 つのドキュメント内でも:
    • 名前空間付きの要素には必ずしも接頭辞さえありません
    • 同じプレフィックスが常に同じ名前空間 uri にマップされるとは限りません
    • 同じ名前空間 uri が常に同じプレフィックスを持つとは限りません

参考までに: 特定の要素のスコープ内のプレフィックス マッピングを取得する場合はelem.nsmap 、lxml で試してください。また、lxml.etree のiterparse および iterwalkメソッドを使用して、名前空間宣言を「通知」することができます。

于 2011-04-07T21:18:30.297 に答える