64

デフォルトの名前空間を持つXMLドキュメントがあります。XPathNavigatorを使用して、Xpathを使用してノードのセットを次のように選択しています。

XmlElement myXML = ...;  
XPathNavigator navigator = myXML.CreateNavigator();
XPathNodeIterator result = navigator.Select("/outerelement/innerelement");

結果が返されません。これは、名前空間を指定していないためだと思います。選択に名前空間を含めるにはどうすればよいですか?

4

14 に答える 14

86

まず、ナビゲーターは必要ありません。SelectNodes/SelectSingleNodeで十分です。

ただし、名前空間マネージャーが必要になる場合があります。例:

XmlElement el = ...; //TODO
XmlNamespaceManager nsmgr = new XmlNamespaceManager(
    el.OwnerDocument.NameTable);
nsmgr.AddNamespace("x", el.OwnerDocument.DocumentElement.NamespaceURI);
var nodes = el.SelectNodes(@"/x:outerelement/x:innerelement", nsmgr);
于 2009-02-25T12:40:05.540 に答える
49

XPath ビジュアライザー ツールを試してみてください。

XPathVisualizerは無料で使いやすいです。

代替テキスト

重要: Windows 7/8 を使用していて、[ファイル]、[編集]、[ヘルプ] メニューの項目が表示されない場合は、ALT キーを押してください。

于 2009-12-12T17:17:34.043 に答える
29

簡単なハック ソリューションを探している人にとって、特にXML を知っていて、名前空間などについて心配する必要がない場合は、ファイルを文字列に読み込んで、この厄介な小さな「機能」を回避できます。攻撃属性の置き換え:

XmlDocument doc = new XmlDocument();
string fileData = File.ReadAllText(fileName);
fileData = fileData.Replace(" xmlns=\"", " whocares=\"");
using (StringReader sr = new StringReader(fileData))
{
   doc.Load(sr);
}

XmlNodeList nodeList = doc.SelectNodes("project/property");

これは、単一のファイルを扱っているときに、デフォルトの名前空間のプレフィックスを必要とする他のすべてのナンセンスよりも簡単だと思います。お役に立てれば。

于 2014-04-18T17:40:57.493 に答える
20

名前空間を持つ XML で (ナビゲーターまたは SelectNodes/SelectSingleNode を介して) .NET で XPath を使用する場合は、次のことを行う必要があります。

  • 独自の XmlNamespaceManager を提供する

  • 名前空間にある XPath 式のすべての要素に明示的にプレフィックスを付けます

後者は (以下にリンクされている MS ソースから言い換えたものです): XPath 1.0 はデフォルトの名前空間の仕様 (xmlns="some_namespace") を無視するためです。したがって、プレフィックスなしで要素名を使用すると、null 名前空間が想定されます。

そのため、XPath の .NET 実装は、XmlNamespaceManager のプレフィックス String.Empty を持つ名前空間を無視し、常に null 名前空間を使用します。

詳細については、「 XmlNamespaceManager と UndefinedXsltContext がデフォルトの名前空間を処理しない」を参照してください。

デフォルトの名前空間宣言を追加するだけでは古い XPath 名前空間を認識できないため、この「機能」は非常に不便だと思いますが、それが機能する方法です。

于 2010-05-26T08:15:34.320 に答える
8

次のように、XmlNamespaceManager を使用せずに XPath ステートメントを使用できます。

...
navigator.Select("//*[ local-name() = 'innerelement' and namespace-uri() = '' ]")
...

これは、既定の名前空間が定義された XML 内の要素を選択する簡単な方法です。

ポイントは、使用することです:

namespace-uri() = ''

接頭辞を使用せずにデフォルトの名前空間を持つ要素を見つけます。

于 2012-03-14T13:19:20.647 に答える
6

空白のデフォルト名前空間で同様の問題が発生しました。この XML の例では、名前空間プレフィックスを持つ要素と、名前空間プレフィックスを持たない単一の要素 (DataBlock) が混在しています。

<src:SRCExample xmlns="urn:some:stuff:here" xmlns:src="www.test.com/src" xmlns:a="www.test.com/a" xmlns:b="www.test.com/b">
 <DataBlock>
  <a:DocID>
   <a:IdID>7</a:IdID>
  </a:DocID>
  <b:Supplimental>
   <b:Data1>Value</b:Data1>
   <b:Data2/>
   <b:Extra1>
    <b:More1>Value</b:More1>
   </b:Extra1>
  </b:Supplimental>
 </DataBlock>
</src:SRCExample>

XPath Visualizer で機能する XPath を使用しようとしましたが、私のコードでは機能しませんでした。

  XmlDocument doc = new XmlDocument();
  doc.Load( textBox1.Text );
  XPathNavigator nav = doc.DocumentElement.CreateNavigator();
  XmlNamespaceManager nsman = new XmlNamespaceManager( nav.NameTable );
  foreach ( KeyValuePair<string, string> nskvp in nav.GetNamespacesInScope( XmlNamespaceScope.All ) ) {
    nsman.AddNamespace( nskvp.Key, nskvp.Value );
  }

  XPathNodeIterator nodes;

  XPathExpression failingexpr = XPathExpression.Compile( "/src:SRCExample/DataBlock/a:DocID/a:IdID" );
  failingexpr.SetContext( nsman );
  nodes = nav.Select( failingexpr );
  while ( nodes.MoveNext() ) {
    string testvalue = nodes.Current.Value;
  }

XPath の「DataBlock」要素に絞り込みましたが、DataBlock 要素を単純にワイルドカード化する以外に機能させることはできませんでした。

  XPathExpression workingexpr = XPathExpression.Compile( "/src:SRCExample/*/a:DocID/a:IdID" );
  failingexpr.SetContext( nsman );
  nodes = nav.Select( failingexpr );
  while ( nodes.MoveNext() ) {
    string testvalue = nodes.Current.Value;
  }

多くの頭を悩ませてグーグルで調べた後(ここにたどり着きました)、XmlNamespaceManagerローダーでデフォルトの名前空間を次のように変更して直接取り組むことにしました。

  foreach ( KeyValuePair<string, string> nskvp in nav.GetNamespacesInScope( XmlNamespaceScope.All ) ) {
    nsman.AddNamespace( nskvp.Key, nskvp.Value );
    if ( nskvp.Key == "" ) {
      nsman.AddNamespace( "default", nskvp.Value );
    }
  }

したがって、「デフォルト」と「」は同じ名前空間を指します。これを行うと、XPath "/src:SRCExample/default:DataBlock/a:DocID/a:IdID" が希望どおりの結果を返しました。うまくいけば、これが他の人の問題を明確にするのに役立ちます。

于 2011-09-27T19:50:15.710 に答える
5

外部要素と内部要素の名前空間が異なる場合

XmlNamespaceManager manager = new XmlNamespaceManager(myXmlDocument.NameTable);
                            manager.AddNamespace("o", "namespaceforOuterElement");
                            manager.AddNamespace("i", "namespaceforInnerElement");
string xpath = @"/o:outerelement/i:innerelement"
// For single node value selection
XPathExpression xPathExpression = navigator.Compile(xpath );
string reportID = myXmlDocument.SelectSingleNode(xPathExpression.Expression, manager).InnerText;

// For multiple node selection
XmlNodeList myNodeList= myXmlDocument.SelectNodes(xpath, manager);
于 2009-02-25T12:55:09.247 に答える
3

私の場合、プレフィックスを追加することは実用的ではありませんでした。実行時に決定されたxmlまたはxpathが多すぎます。最終的に、XmlNodeでメソッドを拡張しました。これはパフォーマンスが最適化されておらず、おそらくすべてのケースを処理できるわけではありませんが、これまでのところ機能しています。

    public static class XmlExtenders
{

    public static XmlNode SelectFirstNode(this XmlNode node, string xPath)
    {
        const string prefix = "pfx";
        XmlNamespaceManager nsmgr = GetNsmgr(node, prefix);
        string prefixedPath = GetPrefixedPath(xPath, prefix);
        return node.SelectSingleNode(prefixedPath, nsmgr);
    }

    public static XmlNodeList SelectAllNodes(this XmlNode node, string xPath)
    {
        const string prefix = "pfx";
        XmlNamespaceManager nsmgr = GetNsmgr(node, prefix);
        string prefixedPath = GetPrefixedPath(xPath, prefix);
        return node.SelectNodes(prefixedPath, nsmgr);
    }

    public static XmlNamespaceManager GetNsmgr(XmlNode node, string prefix)
    {
        string namespaceUri;
        XmlNameTable nameTable;
        if (node is XmlDocument)
        {
            nameTable = ((XmlDocument) node).NameTable;
            namespaceUri = ((XmlDocument) node).DocumentElement.NamespaceURI;
        }
        else
        {
            nameTable = node.OwnerDocument.NameTable;
            namespaceUri = node.NamespaceURI;
        }
        XmlNamespaceManager nsmgr = new XmlNamespaceManager(nameTable);
        nsmgr.AddNamespace(prefix, namespaceUri);
        return nsmgr;
    }

    public static string GetPrefixedPath(string xPath, string prefix)
    {
        char[] validLeadCharacters = "@/".ToCharArray();
        char[] quoteChars = "\'\"".ToCharArray();

        List<string> pathParts = xPath.Split("/".ToCharArray()).ToList();
        string result = string.Join("/",
                                    pathParts.Select(
                                        x =>
                                        (string.IsNullOrEmpty(x) ||
                                         x.IndexOfAny(validLeadCharacters) == 0 ||
                                         (x.IndexOf(':') > 0 &&
                                          (x.IndexOfAny(quoteChars) < 0 || x.IndexOfAny(quoteChars) > x.IndexOf(':'))))
                                            ? x
                                            : prefix + ":" + x).ToArray());
        return result;
    }
}

次に、コードで次のようなものを使用します

        XmlDocument document = new XmlDocument();
        document.Load(pathToFile);
        XmlNode node = document.SelectFirstNode("/rootTag/subTag");

お役に立てれば

于 2010-01-13T06:30:27.690 に答える
1

上記の SpikeDog で説明されているハックだが便利なアプローチを使用しました。パイプを使用して複数のパスを結合する xpath 式を投げるまでは、非常にうまく機能しました。

そこで、正規表現を使用して書き直し、共有したいと思いました:

public string HackXPath(string xpath_, string prefix_)
{
    return System.Text.RegularExpressions.Regex.Replace(xpath_, @"(^(?![A-Za-z0-9\-\.]+::)|[A-Za-z0-9\-\.]+::|[@|/|\[])(?'Expression'[A-Za-z][A-Za-z0-9\-\.]*)", x =>
                {
                    int expressionIndex = x.Groups["Expression"].Index - x.Index;
                    string before = x.Value.Substring(0, expressionIndex);
                    string after = x.Value.Substring(expressionIndex, x.Value.Length - expressionIndex);
                    return String.Format("{0}{1}:{2}", before, prefix_, after);
                });
}
于 2010-10-20T16:11:13.443 に答える
1

または、誰かが私のように XPathDocument を使用する必要がある場合:

XPathDocument xdoc = new XPathDocument(file);
XPathNavigator nav = xdoc.CreateNavigator();
XmlNamespaceManager nsmgr = new XmlNamespaceManager(nav.NameTable);
nsmgr.AddNamespace("y", "http://schemas.microsoft.com/developer/msbuild/2003");
XPathNodeIterator nodeIter = nav.Select("//y:PropertyGroup", nsmgr);
于 2012-07-03T13:24:29.017 に答える
0

この場合、名前空間の解決が問題の原因である可能性がありますが、XPath 式自体が正しくない可能性もあります。最初にそれを評価することをお勧めします。

XPathNavigator を使用したコードを次に示します。

//xNav is the created XPathNavigator.
XmlNamespaceManager mgr = New XmlNamespaceManager(xNav.NameTable);
mgr.AddNamespace("prefix", "http://tempuri.org/");

XPathNodeIterator result = xNav.Select("/prefix:outerelement/prefix:innerelement", mgr);
于 2009-02-25T12:48:40.620 に答える