74

少なくとも .Net Frameworkでは、XPath クエリを実行するときに名前空間 (またはかなり不格好で冗長なXPath 述語/関数/その他)を処理するために を使用する必要がある理由について、私はちょっとドライに思いつきました。. 名前空間が必要な理由、または少なくとも有益である理由は理解できますが、なぜそれほど複雑なのですか?XmlNamespaceManager[local-name()=...

単純な XML ドキュメント (名前空間なし) をクエリするには...

<?xml version="1.0" encoding="ISO-8859-1"?>
<rootNode>
   <nodeName>Some Text Here</nodeName>
</rootNode>

...次のようなものを使用できますdoc.SelectSingleNode("//nodeName")(一致し<nodeName>Some Text Here</nodeName>ます)

ミステリー #1 :私の最初の煩わしさ - 私の理解が正しければ - 親/ルートタグ (子ノードタグの一部として使用されているかどうかにかかわらず) に名前空間参照を追加するだけです:

<?xml version="1.0" encoding="ISO-8859-1"?>
<rootNode xmlns="http://example.com/xmlns/foo">
   <nodeName>Some Text Here</nodeName>
</rootNode>

...同じ結果を得るには、さらに数行のコードが必要です。

Dim nsmgr As New XmlNamespaceManager(doc.NameTable)
nsmgr.AddNamespace("ab", "http://example.com/xmlns/foo")
Dim desiredNode As XmlNode = doc.SelectSingleNode("//ab:nodeName", nsmgr)

...基本的に、存在しないプレフィックス (" ab") を夢見て、プレフィックスを使用していないノードを見つけます。これはどのように理にかなっていますか?(概念的に)何が間違っていdoc.SelectSingleNode("//nodeName")ますか?

ミステリー #2 : たとえば、プレフィックスを使用する XML ドキュメントがあるとします。

<?xml version="1.0" encoding="ISO-8859-1"?>
<rootNode xmlns:cde="http://example.com/xmlns/foo" xmlns:feg="http://example.com/xmlns/bar">
   <cde:nodeName>Some Text Here</cde:nodeName>
   <feg:nodeName>Some Other Value</feg:nodeName>
   <feg:otherName>Yet Another Value</feg:otherName>
</rootNode>

...私が正しく理解している場合XmlNamespaceManager、単一のノードのクエリを作成するには、両方の名前空間をに追加する必要があります...

Dim nsmgr As New XmlNamespaceManager(doc.NameTable)
nsmgr.AddNamespace("cde", "http://example.com/xmlns/foo")
nsmgr.AddNamespace("feg", "http://example.com/xmlns/bar")
Dim desiredNode As XmlNode = doc.SelectSingleNode("//feg:nodeName", nsmgr)

... この場合、なぜ (概念的に) 名前空間マネージャーが必要なのですか?

******以下のコメントに編集済み****

編集が追加されました: 私の修正され洗練された質問は、ほとんどの場合であると思われる XmlNamespaceManager の明らかな冗長性と、URI へのプレフィックスのマッピングを指定する名前空間マネージャーの使用に基づいています。

名前空間プレフィックス ("cde") から名前空間 URI (" http://example.com/xmlns/foo ") への直接マッピングがソース ドキュメントで明示的に記述されている場合:

...<rootNode xmlns:cde="http://example.com/xmlns/foo"...

プログラマーがクエリを作成する前にそのマッピングを再作成する必要があるという概念は何ですか?

4

6 に答える 6

21

基本的なポイント (上記の Kevが指摘したように) は、名前空間 URI が名前空間の重要な部分であり、名前空間プレフィックスではなく、プレフィックスは「任意の便宜」であるということです。

なぜ名前空間マネージャーが必要なのかというと、ドキュメントを使って解決する魔法があるのではなく、2 つの理由が考えられます。

理由1

あなたの例のように、documentElementに名前空間宣言のみを追加することが許可されている場合、 selectSingleNode が定義されているものをそのまま使用するのは簡単です。

ただし、ドキュメント内の任意の要素に名前空間プレフィックスを定義できます。名前空間プレフィックスは、ドキュメント内の特定の名前空間に一意にバインドされるわけではありません。次の例を考えてみましょう

<w xmlns:a="mynamespace">
  <a:x>
    <y xmlns:a="myOthernamespace">
      <z xmlns="mynamespace">
      <b:z xmlns:b="mynamespace">
      <z xmlns="myOthernamespace">
      <b:z xmlns:b="myOthernamespace">
    </y>
  </a:x>
</w>

この例では、何が必要で、何が//z返さ//a:z//b:zますか? ある種の外部名前空間マネージャーがなければ、それをどのように表現しますか?

理由 2

これにより、使用中の名前空間プレフィックスについて何も知る必要なく、同等のドキュメントに対して同じ XPath 式を再利用できます。

myXPathExpression = "//z:y"
doc1.selectSingleNode(myXPathExpression);
doc2.selectSingleNode(myXPathExpression);

ドキュメント1:

<x>
  <z:y xmlns:z="mynamespace" />
</x>

ドキュメント2:

<x xmlns"mynamespace">
  <y>
</x>

名前空間マネージャーなしでこの後者の目標を達成するには、各ドキュメントを調べて、それぞれのカスタム XPath 式を作成する必要があります。

于 2011-10-27T19:58:36.507 に答える
14

理由は簡単です。XPath クエリで使用するプレフィックスと xml ドキュメントで宣言されたプレフィックスの間には、必要な接続はありません。例を挙げると、次の xml は意味的に同等です。

<aaa:root xmlns:aaa="http://someplace.org">
 <aaa:element>text</aaa:element>
</aaa:root>

  <bbb:root xmlns:bbb="http://someplace.org">
     <bbb:element>text</bbb:element>
  </bbb:root>

" ccc:root/ccc:element" クエリは、ネームスペース マネージャにそのためのマッピングがある場合、両方のインスタンスに一致します。

nsmgr.AddNamespace("ccc", "http://someplace.org")

.NET 実装は、xml で使用されるリテラル プレフィックスを気にしません。クエリ リテラルに対して定義されたプレフィックスがあり、名前空間の値がドキュメントの実際の値と一致することだけです。これは、使用されるドキュメント間でプレフィックスが異なる場合でも、一定のクエリ式を持つ必要があり、一般的なケースの正しい実装です。

于 2011-10-05T14:23:54.720 に答える
13

私が知る限り、次のようなドキュメントがある場合、接頭辞付きのノードXmlNamespaceManagerを取得するために を手動で定義する必要がある正当な理由はありません。abc

<itemContainer xmlns:abc="http://abc.com" xmlns:def="http://def.com">
    <abc:nodeA>...</abc:nodeA>
    <def:nodeB>...</def:nodeB>
    <abc:nodeC>...</abc:nodeC>
</itemContainer>

xmlns:abcMicrosoftは、親ノードで既に指定されているものを検出するために何かを書くことを単に気にすることができませんでした。私は間違っている可能性があります。もしそうなら、この回答に対するコメントを歓迎しますので、更新できます。

しかし、このブログ投稿は私の疑いを裏付けているようです。基本的には、属性を手動で定義しXmlNamespaceManager、属性を手動で繰り返し、xmlns:それぞれを名前空間マネージャーに追加する必要があることを示しています。マイクロソフトがこれを自動的に行うことができなかった理由はわかりません。

ソースの属性にXmlNamespaceManager基づいてを自動的に生成するために、そのブログ投稿に基づいて作成したメソッドを次に示します。xmlns:XmlDocument

/// <summary>
/// Creates an XmlNamespaceManager based on a source XmlDocument's name table, and prepopulates its namespaces with any 'xmlns:' attributes of the root node.
/// </summary>
/// <param name="sourceDocument">The source XML document to create the XmlNamespaceManager for.</param>
/// <returns>The created XmlNamespaceManager.</returns>
private XmlNamespaceManager createNsMgrForDocument(XmlDocument sourceDocument)
{
    XmlNamespaceManager nsMgr = new XmlNamespaceManager(sourceDocument.NameTable);

    foreach (XmlAttribute attr in sourceDocument.SelectSingleNode("/*").Attributes)
    {
        if (attr.Prefix == "xmlns")
        {
            nsMgr.AddNamespace(attr.LocalName, attr.Value);
        }
    }

    return nsMgr;
}

そして、私はそれを次のように使用します:

XPathNavigator xNav = xmlDoc.CreateNavigator();
XPathNodeIterator xIter = xNav.Select("//abc:NodeC", createNsMgrForDocument(xmlDoc));
于 2011-10-05T13:34:06.817 に答える
4

ポイント1に答えます:

XML ドキュメントにデフォルトの名前空間を設定すると、名前空間の接頭辞がなくても、ノードは次のようになります。

<rootNode xmlns="http://someplace.org">
   <nodeName>Some Text Here</nodeName>
</rootNode>

「空の」名前空間にはありません。XPath を使用してこれらのノードを参照する何らかの方法が必要なため、それらを参照するためのプレフィックスを作成します。

ポイント2に答えるには:

<rootNode xmlns:cde="http://someplace.org" xmlns:feg="http://otherplace.net">
   <cde:nodeName>Some Text Here</cde:nodeName>
   <feg:nodeName>Some Other Value</feg:nodeName>
   <feg:otherName>Yet Another Value</feg:otherName>
</rootNode>

インスタンス ドキュメントの内部では、名前空間に存在するノードはノード名と長い名前空間名で格納され、(W3C の用語では)拡張名と呼ばれます。

たとえば、<cde:nodeName>基本的に として保存され<http://someplace.org:nodeName>ます。名前空間プレフィックスは人間にとって任意の便利なものであるため、XML を入力したり読み取ったりする必要がある場合に、これを行う必要はありません。

<rootNode>
   <http://someplace.org:nodeName>Some Text Here</http://someplace.org:nodeName>
   <http://otherplace.net:nodeName>Some Other Value</http://otherplace.net:nodeName>
   <http://otherplace.net:otherName>Yet Another Value</http://otherplace.net:otherName>
</rootNode>

XML ドキュメントが検索されるとき、フレンドリ プレフィックスでは検索されません。検索は名前空間 URI によって行われるため、渡された名前空間テーブルを介して XPath に名前空間を伝える必要がありますXmlNamespaceManager

于 2011-08-24T15:38:43.470 に答える
3

このスレッドは、名前空間の問題をより明確に理解するのに役立ちました。ありがとう。Jez のコードを見たとき、私がプログラムしたよりも優れたソリューションのように見えたので、試してみました。しかし、私はそれでいくつかの欠点を発見しました。書かれているように、ルートノードのみを検索し (ただし、名前空間はどこにでもリストできます)、デフォルトの名前空間は処理しません。私は彼のコードを変更してこれらの問題に対処しようとしましたが、役に立ちませんでした。

これがその関数の私のバージョンです。正規表現を使用して、ファイル全体の名前空間のマッピングを見つけます。デフォルトの名前空間で動作し、任意の接頭辞「ns」を与えます。同じ名前空間の複数の発生を処理します。

private XmlNamespaceManager CreateNamespaceManagerForDocument(XmlDocument document)
{
    var nsMgr = new XmlNamespaceManager(document.NameTable);

    // Find and remember each xmlns attribute, assigning the 'ns' prefix to default namespaces.
    var nameSpaces = new Dictionary<string, string>();
    foreach (Match match in new Regex(@"xmlns:?(.*?)=([\x22\x27])(.+?)\2").Matches(document.OuterXml))
        nameSpaces[match.Groups[1].Value + ":" + match.Groups[3].Value] = match.Groups[1].Value == "" ? "ns" : match.Groups[1].Value;

    // Go through the dictionary, and number non-unique prefixes before adding them to the namespace manager.
    var prefixCounts = new Dictionary<string, int>();
    foreach (var namespaceItem in nameSpaces)
    {
        var prefix = namespaceItem.Value;
        var namespaceURI = namespaceItem.Key.Split(':')[1];
        if (prefixCounts.ContainsKey(prefix)) 
            prefixCounts[prefix]++; 
        else 
            prefixCounts[prefix] = 0;
        nsMgr.AddNamespace(prefix + prefixCounts[prefix].ToString("#;;"), namespaceURI);
    }
    return nsMgr;
}
于 2013-12-04T00:06:44.667 に答える
3

XmlNamespaceManager インスタンスに URI とプレフィックスのペアを登録して、参照している特定の「nodeName」ノード ( http://someplace.org」または「http: //otherplace.net".

XPath クエリを実行している場合、具体的なプレフィックス名は重要ではないことに注意してください。これもうまくいくと思います:

Dim nsmgr As New XmlNamespaceManager(doc.NameTable)
nsmgr.AddNamespace("any", "http://someplace.org")
nsmgr.AddNamespace("thing", "http://otherplace.net")
Dim desiredNode As XmlNode = doc.SelectSingleNode("//thing:nodeName", nsmgr)

SelectSingleNode() は、XPath 式のプレフィックスと名前空間 URI の間の接続のみを必要とします。

于 2011-08-26T13:15:18.587 に答える