web-dev-qa-db-ja.com

接頭辞のないデフォルトの名前空間でXPathを使用するにはどうすればよいですか?

このドキュメントからすべてのMyNodeを照会するためのXPath(C#APIでXDocument.XPathSelectElements(xpath、nsman)が重要な場合)とは何ですか?

_<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <MyNode xmlns="lcmp" attr="true">
    <subnode />
  </MyNode>
</configuration>
_
  • 名前空間を無視するので間違っている_/configuration/MyNode_を試してみました。
  • 私は_/configuration/lcmp:MyNode_を試しましたが、lcmpはURIであり、接頭辞ではないため、間違っています。
  • _/configuration/{lcmp}MyNode_が原因で失敗した_Additional information: '/configuration/{lcmp}MyNode' has an invalid token._を試してみました

編集:一部の回答者が示唆したように、mgr.AddNamespace("df", "lcmp");を使用できません。そのためには、XML解析プログラムが、事前に使用する予定のすべての名前空間を知っている必要があります。これはすべてのソースファイルに適用できることを意図しているため、手動でプレフィックスを追加する名前空間がわかりません。 _{my uri}_はXPath構文のようですが、Microsoftはそれを実装する気になりませんでした...本当ですか?

34
Scott Stafford

configuration要素は名前のない名前空間にあり、MyNodeは名前空間接頭辞なしでlcmp名前空間にバインドされています。

この[〜#〜] xpath [〜#〜]ステートメントを使用すると、MyNode名前空間を宣言したり、名前空間接頭辞を使用したりせずに、lcmp要素をアドレス指定できますXPATH:

_/configuration/*[namespace-uri()='lcmp' and local-name()='MyNode']
_

これは、configurationの子であるすべての要素と一致し、述語ファイラーを namespace-uri() および local-name() 関数とともに使用して制限しますMyNode要素に追加します。

要素に使用する名前空間URIがわからない場合は、[〜#〜] xpath [〜#〜]より一般的で、local-name()に一致します:

_/configuration/*[local-name()='MyNode']
_

ただし、たまたま同じ名前を使用する異なるボキャブラリ(異なる名前空間URIにバインドされている)の異なる要素に一致するリスクがあります。

37
Mads Hansen

次のようにXmlNamespaceManagerを使用する必要があります。

   XDocument doc = XDocument.Load(@"..\..\XMLFile1.xml");
   XmlNamespaceManager mgr = new XmlNamespaceManager(new NameTable());
   mgr.AddNamespace("df", "lcmp");
   foreach (XElement myNode in doc.XPathSelectElements("configuration/df:MyNode", mgr))
   {
       Console.WriteLine(myNode.Attribute("attr").Value);
   }
12
Martin Honnen

XPathは(意図的に)XMLドキュメントにのみ存在する不明な名前空間に同じXPath式を使用する場合のために設計されていません。事前に名前空間を把握し、XPathプロセッサに名前空間を宣言し、式でその名前を使用する必要があります。 MartinとDanの回答は、C#でこれを行う方法を示しています。

この問題の理由は XML名前空間 仕様で最もよく表現されています。

単一のXMLドキュメントに、複数のソフトウェアモジュールで定義され、使用される要素と属性(ここでは「マークアップボキャブラリ」と呼ばれる)が含まれる可能性があるExtensible Markup Language(XML)のアプリケーションを想定しています。これの1つの動機はモジュール性です。このようなマークアップボキャブラリが存在し、十分に理解されていて、有用なソフトウェアが利用可能な場合、このマークアップを再発明するよりも再利用することをお勧めします。

複数のマークアップボキャブラリを含むそのようなドキュメントは、認識と衝突の問題を引き起こします。ソフトウェアモジュールは、他のソフトウェアパッケージ向けのマークアップが同じ要素名または属性名を使用するときに発生する「衝突」に直面しても、それらが処理するように設計された要素と属性を認識できる必要があります。

これらの考慮事項では、異なるマークアップボキャブラリの名前間の衝突を回避するために、ドキュメントの構成に名前を作成する必要があります。この仕様では、要素と属性に拡張名を割り当てることでこれを実現するメカニズムであるXML名前空間について説明します。

つまり、名前空間は、ドキュメントが話していることを確実に理解するために使用されることになっています。つまり、_<head>_要素は、XHTMLドキュメントのプリアンブルまたはAnatomyMLドキュメントの誰かの頭について話しているのですか?名前空間にとらわれない「想定」されることは決してありません。それは、XMLボキャブラリで定義すべき最初のことです。

あなたが望むことをすることは可能であるべきですが、単一のXPath式でそれを行うことができるとは思いません。まず最初に、ドキュメントをあちこち調べてすべての名前空間URIを抽出し、次にこれらを名前空間マネージャーに追加して、必要な実際のXPath式を実行します(ドキュメントの名前空間の配布について何か知っておく必要があります)ポイント、または実行する多くの式があります)。 XPath以外のもの(たとえば、DOMまたはSAXのようなAPI)を使用して名前空間URIを見つけるのが最善だと思いますが、XPath名前空間軸(XPath 1.0の場合)を探索することもできます _namespace-uri-from-QName_ 関数(XPath 2.0の場合)またはOlegの"configuration/*[local-name() = 'MyNode']"などの式を使用します。とにかく、私はあなたの最善の策は名前空間にとらわれないXPathを書くことを避けようとすることだと思います!事前に名前空間を知らないのはなぜですか?一致させるつもりのないものの一致をどのように回避しますか?

編集-あなたは名前空間URIを知っていますか?

だからあなたの質問は私たち全員を混乱させたことがわかります。どうやらあなたは名前空間URIを知っていますが、XMLドキュメントで使用されている名前空間接頭辞を知りません。実際、この場合、名前空間接頭辞は使用されず、URIはそれが定義されているデフォルトの名前空間になります。知っておくべき重要なことは、選択されたプレフィックス(またはプレフィックスの欠如)はXPath式(および一般にXML解析)とは無関係であることです。 prefix/xmlns属性は、ドキュメントがテキストとして表現されている場合にノードを名前空間URIに関連付ける1つの方法にすぎません。名前空間の接頭辞を明確にしようとする this answer を確認することをお勧めします。

XML文書は、パーサーが考えるのと同じように考える必要があります。各ノードには名前空間URIとローカル名があります。名前空間の接頭辞/継承ルールは、URIを何度も入力する手間を省くだけです。これを書き留める1つの方法は、クラーク記法です。つまり、{ http://www.example.com/namespace/example } LocalNodeNameと記述しますが、この記法は通常、ドキュメンテーションにのみ使用されます- XPathはこの表記について何も知りません。

代わりに、XPathは独自の名前空間接頭辞を使用します。_/ns1:root/ns2:node_のようなもの。しかし、これらは完全に分離されており、元のXMLドキュメントで使用される可能性のある接頭辞とは何の関係もありません。すべてのXPath実装には、それ自体のプレフィックスを名前空間URIにマップする方法があります。 C#の実装ではXmlNamespaceManagerを使用し、Perlではハッシュを指定します。xmllintはコマンドライン引数を取ります...したがって、必要なのは、知っている名前空間URIの任意のプレフィックスを作成し、これを使用することだけです。 XPath式のプレフィックス。どのプレフィックスを使用するかは関係ありません。XMLでは、URIとlocalNameの組み合わせを気にするだけです。

覚えておかなければならないもう1つのことは、XPathが名前空間の継承を行わないことです。ネームスペースが継承、xmlns属性、またはネームスペースプレフィックスに由来するかどうかに関係なく、ネームスペースを持つすべてのものにプレフィックスを追加する必要があります。また、常にURIとlocalNamesの観点から考える必要がありますが、XMLドキュメントからプレフィックスにアクセスする方法もあります。これらを使用する必要があることはまれです。

7
Andrew Walker

XPathSelectElements拡張メソッドのXPath式で名前空間を利用できるようにする方法の例を次に示します。

using System;
using System.Xml.Linq;
using System.Xml.XPath;
using System.Xml;
namespace XPathExpt
{
 class Program
 {
   static void Main(string[] args)
   {
     XElement cfg = XElement.Parse(
       @"<configuration>
          <MyNode xmlns=""lcmp"" attr=""true"">
            <subnode />
          </MyNode>
         </configuration>");
     XmlNameTable nameTable = new NameTable();
     var nsMgr = new XmlNamespaceManager(nameTable);
     // Tell the namespace manager about the namespace
     // of interest (lcmp), and give it a prefix (pfx) that we'll
     // use to refer to it in XPath expressions. 
     // Note that the prefix choice is pretty arbitrary at 
     // this point.
     nsMgr.AddNamespace("pfx", "lcmp");
     foreach (var el in cfg.XPathSelectElements("//pfx:MyNode", nsMgr))
     {
         Console.WriteLine("Found element named {0}", el.Name);
     }
   }
 }
}
4
Dan Blanchard

私は彼の答えである@ mads-hansenが好きなので、これらの汎用ユーティリティクラスメンバーを作成しました。

    /// <summary>
    /// Gets the <see cref="XNode" /> into a <c>local-name()</c>, XPath-predicate query.
    /// </summary>
    /// <param name="childElementName">Name of the child element.</param>
    /// <returns></returns>
    public static string GetLocalNameXPathQuery(string childElementName)
    {
        return GetLocalNameXPathQuery(namespacePrefixOrUri: null, childElementName: childElementName, childAttributeName: null);
    }

    /// <summary>
    /// Gets the <see cref="XNode" /> into a <c>local-name()</c>, XPath-predicate query.
    /// </summary>
    /// <param name="namespacePrefixOrUri">The namespace prefix or URI.</param>
    /// <param name="childElementName">Name of the child element.</param>
    /// <returns></returns>
    public static string GetLocalNameXPathQuery(string namespacePrefixOrUri, string childElementName)
    {
        return GetLocalNameXPathQuery(namespacePrefixOrUri, childElementName, childAttributeName: null);
    }

    /// <summary>
    /// Gets the <see cref="XNode" /> into a <c>local-name()</c>, XPath-predicate query.
    /// </summary>
    /// <param name="namespacePrefixOrUri">The namespace prefix or URI.</param>
    /// <param name="childElementName">Name of the child element.</param>
    /// <param name="childAttributeName">Name of the child attribute.</param>
    /// <returns></returns>
    /// <remarks>
    /// This routine is useful when namespace-resolving is not desirable or available.
    /// </remarks>
    public static string GetLocalNameXPathQuery(string namespacePrefixOrUri, string childElementName, string childAttributeName)
    {
        if (string.IsNullOrEmpty(childElementName)) return null;

        if (string.IsNullOrEmpty(childAttributeName))
        {
            return string.IsNullOrEmpty(namespacePrefixOrUri) ?
                string.Format("./*[local-name()='{0}']", childElementName)
                :
                string.Format("./*[namespace-uri()='{0}' and local-name()='{1}']", namespacePrefixOrUri, childElementName);
        }
        else
        {
            return string.IsNullOrEmpty(namespacePrefixOrUri) ?
                string.Format("./*[local-name()='{0}']/@{1}", childElementName, childAttributeName)
                :
                string.Format("./*[namespace-uri()='{0}' and local-name()='{1}']/@{2}", namespacePrefixOrUri, childElementName, childAttributeName);
        }
    }
1
rasx

Xpath 2.0 +ライブラリの例:

_using Wmhelp.XPath2;_

doc.XPath2SelectElements("/*:configuration/*:MyNode");

見る :

XPathおよびXSLT 2.0 for .NET?

1
Akli