web-dev-qa-db-ja.com

XPathはXML名前空間をどのように処理しますか?

XPathはXML名前空間をどのように処理しますか?

私が使用する場合

/IntuitResponse/QueryResponse/Bill/Id

以下のXMLドキュメントを解析するには、0個のノードを取得します。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<IntuitResponse xmlns="http://schema.intuit.com/finance/v3" 
                time="2016-10-14T10:48:39.109-07:00">
    <QueryResponse startPosition="1" maxResults="79" totalCount="79">
        <Bill domain="QBO" sparse="false">
            <Id>=1</Id>
        </Bill>
    </QueryResponse>
</IntuitResponse>

ただし、XPathで名前空間を指定していません(つまり、http://schema.intuit.com/finance/v3は、パスの各トークンのプレフィックスではありません)。明示的に指定しない場合、XPathはどのIdが必要かを知ることができますか?この場合(名前空間が1つしかないため)XPathはxmlnsを完全に無視して逃げることができると思います。しかし、複数の名前空間がある場合、事態はgetいものになる可能性があります。

26
Adam

XPathで名前空間を定義する (推奨)

XPath自体には、名前空間プレフィックスを名前空間にバインドする方法がありません。このような機能は、ホスティングライブラリによって提供されます。

これらの機能を使用し、必要に応じてXML要素と属性名を修飾するために使用できる名前空間プレフィックスを定義することをお勧めします。


XPathホストが名前空間URIへの名前空間プレフィックスバインディングを指定するために提供するさまざまなメカニズムの一部を以下に示します。

XSLT:

_<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:i="http://schema.intuit.com/finance/v3">
   ...
_

Perl( LibXML ):

_my $xc = XML::LibXML::XPathContext->new($doc);
$xc->registerNs('i', 'http://schema.intuit.com/finance/v3');
my @nodes = $xc->findnodes('/i:IntuitResponse/i:QueryResponse');
_

Python( lxml ):

_from lxml import etree
f = StringIO('<IntuitResponse>...</IntuitResponse>')
doc = etree.parse(f)
r = doc.xpath('/i:IntuitResponse/i:QueryResponse', 
              namespaces={'i':'http://schema.intuit.com/finance/v3'})
_

Python( ElementTree ):

_namespaces = {'i': 'http://schema.intuit.com/finance/v3'}
root.findall('/i:IntuitResponse/i:QueryResponse', namespaces)
_

Java(SAX):

_NamespaceSupport support = new NamespaceSupport();
support.pushContext();
support.declarePrefix("i", "http://schema.intuit.com/finance/v3");
_

Java(XPath):

_xpath.setNamespaceContext(new NamespaceContext() {
    public String getNamespaceURI(String prefix) {
      switch (prefix) {
        case "i": return "http://schema.intuit.com/finance/v3";
        // ...
       }
    });
_

xmlstarlet:

_-N i="http://schema.intuit.com/finance/v3"
_

JavaScript:

ユーザー定義の名前空間リゾルバーの実装 を参照してください。

_function nsResolver(prefix) {
  var ns = {
    'i' : 'http://schema.intuit.com/finance/v3'
  };
  return ns[prefix] || null;
}
document.evaluate( '/i:IntuitResponse/i:QueryResponse', 
                   document, nsResolver, XPathResult.ANY_TYPE, 
                   null );
_

PhP:

DOMDocumentを使用した@Tomalakの回答

_$result = new DOMDocument();
$result->loadXML($xml);

$xpath = new DOMXpath($result);
$xpath->registerNamespace("i", "http://schema.intuit.com/finance/v3");

$result = $xpath->query("/i:IntuitResponse/i:QueryResponse");
_

@ IMSoPのPHP SimpleXML名前空間 の標準的なQ/Aも参照してください。

C#:

_XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable);
nsmgr.AddNamespace("i", "http://schema.intuit.com/finance/v3");
XmlNodeList nodes = el.SelectNodes(@"/i:IntuitResponse/i:QueryResponse", nsmgr);
_

VBA:

_xmlNS = "xmlns:i='http://schema.intuit.com/finance/v3'"
doc.setProperty "SelectionNamespaces", xmlNS  
Set queryResponseElement =doc.SelectSingleNode("/i:IntuitResponse/i:QueryResponse")
_

VB.NET:

_xmlDoc = New XmlDocument()
xmlDoc.Load("file.xml")
nsmgr = New XmlNamespaceManager(New XmlNameTable())
nsmgr.AddNamespace("i", "http://schema.intuit.com/finance/v3");
nodes = xmlDoc.DocumentElement.SelectNodes("/i:IntuitResponse/i:QueryResponse",
                                           nsmgr)
_

ルビー(のこぎり):

_puts doc.xpath('/i:IntuitResponse/i:QueryResponse',
                'i' => "http://schema.intuit.com/finance/v3")
_

Nokogiriは名前空間の削除をサポートしていることに注意してください。

_doc.remove_namespaces!
_

ただし、XML名前空間の無効化を妨げる以下の警告を参照してください。


名前空間プレフィックスを宣言したら、それを使用するようにXPathを記述できます。

_/i:IntuitResponse/i:QueryResponse
_

XPathで名前空間を無効にする (非推奨)

別の方法は、local-name()に対してテストする述語を書くことです。

_/*[local-name()='IntuitResponse']/*[local-name()='QueryResponse']/@startPosition
_

または、XPath 2.0の場合:

_/*:IntuitResponse/*:QueryResponse/@startPosition
_

この方法での名前空間のスカートは機能しますが、推奨されません

  • 完全な要素/属性名を指定しません。
  • 異なる名前空間内の要素/属性名を区別しません(名前空間のまさに目的)。名前空間URIを明示的にチェックする追加の述語を追加することにより、この懸念に対処できることに注意してください。1

    _/*[    namespace-uri()='http://schema.intuit.com/finance/v3' 
       and local-name()='IntuitResponse']
    /*[    namespace-uri()='http://schema.intuit.com/finance/v3' 
       and local-name()='QueryResponse']
    /@startPosition
    _

    1Daniel Haleynamespace-uri()のおかげで。

  • 過度に冗長です。

36
kjhughes