2011-04-28 152 views
29

爲什麼這個Xpath不能使用XDocument.XPathSelectElement?XPathSelectElement總是返回null

的Xpath:

//Plugin/UI[1]/PluginPageCategory[1]/Page[1]/Group[1]/CommandRef[2] 

XML

<Plugin xmlns="http://www.MyNamespace.ca/MyPath"> 
    <UI> 
    <PluginPageCategory> 
     <Page> 
     <Group> 
      <CommandRef> 
      <Images> 
      </Images> 
      </CommandRef> 
      <CommandRef> 
      <Images> 
      </Images> 
      </CommandRef> 
     </Group> 
     </Page> 
    </PluginPageCategory> 
    </UI> 
</Plugin> 

C#代碼:

myXDocument.XPathSelectElement("//Plugin/UI[1]/PluginPageCategory[1]/Page[1]/Group[1]/CommandRef[2]", myXDocument.Root.CreateNavigator()); 
+4

xpath查詢中沒有名稱空間信息,這可能是原因。試着縮小這個範圍,刪除XML上的命名空間,看看是否會得到你的結果? – Cumbayah 2011-04-28 13:30:14

+2

這可能是由於命名空間 - 檢查從XML中刪除它是否修復它,如果有,請創建一個NamespaceManager。 – 2011-04-28 13:31:17

+0

@Cumbayah:打我吧! – 2011-04-28 13:32:11

回答

24

當使用的命名空間,這些都必須在XPath查詢也可使用。您的XPath查詢僅適用於沒有名稱空間的元素(可以通過從XML中刪除名稱空間進行驗證)。

這裏是你展示如何創建和傳遞一個命名空間管理器的示例:

var xml = ... XML from your post ...; 

var xmlReader = XmlReader.Create(new StringReader(xml)); // Or whatever your source is, of course. 
var myXDocument = XDocument.Load(xmlReader); 
var namespaceManager = new XmlNamespaceManager(xmlReader.NameTable); // We now have a namespace manager that knows of the namespaces used in your document. 
namespaceManager.AddNamespace("prefix", "http://www.MyNamespace.ca/MyPath"); // We add an explicit prefix mapping for our query. 

var result = myXDocument.XPathSelectElement(
    "//prefix:Plugin/prefix:UI[1]/prefix:PluginPageCategory[1]/prefix:Page[1]/prefix:Group[1]/prefix:CommandRef[2]", 
    namespaceManager 
); // We use that prefix against the elements in the query. 

Console.WriteLine(result); // <CommandRef ...> element is printed. 

希望這有助於。

+42

什麼可怕的代碼。 XML命名空間是真正難看的東西。 – 2011-11-14 06:34:46

+0

如果您必須明確地添加前綴/ ns組合,那麼從XmlReader加載NameTable的意義何在?我假設你這樣做是因爲你不知道文檔用於所需名稱空間的前綴。但是,這不就意味着沒有必要用'NameTable'初始化'XmlNamespaceManager'嗎? – crush 2018-02-28 19:51:15

-2

有一種方法可以在不改變xpath的情況下做到這一點。我找到的解決方案是在將XML解析到XDocument時刪除名稱空間。

這裏是個例:

var regex = @"(xmlns:?[^=]*=[""][^""]*[""])"; 
var myXDocument = XDocument.Parse(Regex.Replace("MyXmlContent", regex, "", RegexOptions.IgnoreCase | RegexOptions.Multiline)) 

現在的命名空間沒有了,這是easyer操縱。

+6

_「現在名稱空間不見了」_您可能沒有一個格式良好的XML文檔... – 2011-04-29 04:47:06

+13

香腸工廠解決方案。 – 2011-04-29 06:21:18

+2

呃...它對我來說非常適合。請注意,當您無法更改xpath時它可能很有用。 – 2011-04-29 13:48:46

14

這應該可能是對@ Cumbayah的帖子的評論,但我似乎無法留下任何評論。

你可能更喜歡使用這樣的東西,而不是使用XmlReader來獲取名稱表。

var xml = ... XML from your post ...; 
var myXDocument = XDocument.Parse(xml); 
var namespaceManager = new XmlNamespaceManager(new NameTable()); 
namespaceManager.AddNamespace("prefix", "http://www.MyNamespace.ca/MyPath"); 

var result = ...; 
+1

只是想指出缺省名稱空間應加前綴的問題。即namespaceManager.AddNamespace(「」,「http://www.MyNamespace.ca/MyPath」)將使DefaultNamespace屬性與預期一致,但它不會被XPathSelectElement使用。誤導! – 2012-08-21 12:52:06

2

你的情況最簡單的方法是使用XPath 節點測試節點名稱和位置選擇的元素。 您的XPath的選擇:

myXDocument.XPathSelectElement("//Plugin/UI[1]/PluginPageCategory[1]/Page[1]/Group[1]/CommandRef[2]", myXDocument.Root.CreateNavigator()); 

可以很容易地轉換成:

myXDocument.XPathSelectElement("/child::node()[local-name()='Plugin']/child::node()[local-name()='UI'][position()=1]/child::node()[local-name()='PluginPageCategory'][position()=1]/child::node()[local-name()='Page'][position()=1]/child::node()[local-name()='Group'][position()=1]/child::node()[local-name()='CommandRef'][position()=2]"); 

沒有必要創建和傳遞的XmlNamespaceManager作爲參數。

+1

太棒了,這個作品很棒。命名空間應該很簡單,但非常棘手,所以這個解決方案非常有用。 – 2017-05-24 02:34:16