36

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

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

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

4

6 に答える 6

38

要素は名前のconfigurationない名前空間にあり、MyNode はlcmp名前空間プレフィックスなしで名前空間にバインドされます。

このXPATHステートメントを使用すると、名前空間を宣言したり、XPATH で名前空間プレフィックスを使用したりMyNodeせずに、要素をアドレス指定できます。lcmp

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

の子である任意の要素に一致configurationし、述語ファイラーnamespace-uri()local-name()関数を使用して要素に制限しMyNodeます。

要素にどの名前空間 URI が使用されるかわからない場合は、XPATHをより一般的にして、次のものに一致させることができlocal-name()ます。

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

ただし、たまたま同じ名前を使用している異なる語彙 (異なる名前空間 uri にバインドされている) の異なる要素を照合するリスクがあります。

于 2010-03-27T16:30:58.300 に答える
12

次のように 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);
   }
于 2010-03-26T18:05:20.470 に答える
8

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

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

単一の XML ドキュメントに、複数のソフトウェア モジュールで定義および使用される要素と属性 (ここでは「マークアップ ボキャブラリ」と呼ぶ) を含めることができる、拡張マークアップ言語 (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の場合) を使用することもできます。またはオレグのような表現を使用する"configuration/*[local-name() = 'MyNode']". とにかく、最善の策は、名前空間にとらわれない XPath を書くことを避けることだと思います! 名前空間を事前に知らないのはなぜですか? 一致させるつもりのないものを一致させないようにするにはどうすればよいでしょうか。

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

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

パーサーが考えるのと同じ方法で XML 文書を考えるようにしてください。各ノードには名前空間 URI とローカル名があります。名前空間プレフィックス/継承ルールにより、URI を何度も入力する手間が省けます。これを書き留める 1 つの方法は、Clark 表記法です。つまり、{ 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 ドキュメントからプレフィックスにアクセスする方法もあります。これらを使用する必要があることはまれです。

于 2010-03-26T19:03:33.367 に答える
4

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);
     }
   }
 }
}
于 2010-03-26T18:06:11.577 に答える
1

Xpath 2.0 + ライブラリの例:

using Wmhelp.XPath2;

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

見る :

.NET 用の XPath と XSLT 2.0?

于 2014-03-19T20:45:32.883 に答える
1

私は@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);
        }
    }
于 2015-11-18T23:13:56.230 に答える