2016-12-16 156 views
0

我想做一個函數,將採取一個XmlNode,並檢查是否每個後續的孩子存在,我有問題。XmlNode檢查是否存在chidnodes列表

的功能應該有類似

private string GetValueForNodeIfExists(XmlNode node, List<string> childNodes){...} 

一個例子說明我想完成什麼簽名: 我需要知道,如果一個節點的孩子(也可能是一個孩子的孩子)存在。 如果我有一個節點有一個名爲「child」的子節點,並且「child」節點有一個名爲「grandchild」的節點,並且該grandchild節點有一個名爲「greatGrandchild」的節點,那麼我想檢查每個序列是否給出null還是不行,那麼檢查以下內容:

node['child'] != null 
node['child']['grandchild'] != null 
node['child']['grandchild']['greatGrandchild'] != null 

我檢查的節點名稱傳遞到函數作爲List<string>其中索引關聯到我檢查節點的深度。例如,在上面的示例中,我將通過的列表是List<string> checkedasd = new List<String> {"child", "grandchild", "greatGrandchild" };

我不知道如何以編程方式追加每個['nodeName']表達式,然後執行表達式。如果我能弄明白這一點,我的策略是將所有東西都放在try塊中,如果我發現了一個空異常,那麼我會知道該節點不存在。

所有幫助表示讚賞

+0

是否每個節點必須直接後代? –

+0

如果您發現與您的模式匹配的多個結果會發生什麼('IEnumerable '?) –

回答

0

所有您需要do是使用XPath:

private string GetValueForNodeIfExists(XmlNode node, List<string> childNodes) 
{ 
    var xpath = string.Join("/", childNodes.ToArray()); 

    var foundNode = node.SelectSingleNode(xpath); 

    return foundNode != null ? foundNode.InnerText : null; 
} 

您還可以擴大你已經擁有並通過數值只是循環,直到你得到一個空值或到達終點:

private string GetValueForNodeIfExists(XmlNode node, List<string> childNodes) 
{ 
    foreach (var nodeName in childNodes) 
    { 
     if (node != null) 
     { 
      node = node[nodeName]; 
     } 
    } 

    return node != null ? node.InnerText : null; 
} 
4

我會用Linq2Xml和XPath

var childNodes = new List<string>() { "child", "grandchild", "greatGrandchild" }; 
var xpath = "//" + string.Join("/", childNodes); 

var xDoc = XDocument.Load(filename); 
var xElem = xDoc.XPathSelectElement(xpath); 

if(xElem!=null) //<--- No need for try- catch block 
    Console.WriteLine(xElem.Value); 

PS:我測試上面的代碼的代碼與下面的XML

<root> 
    <child> 
     <grandchild> 
      <greatGrandchild> 
       a 
      </greatGrandchild> 
     </grandchild> 
    </child> 
</root> 
+0

爲什麼在'XmlNode's已經允許使用XPath時使用Linq2Xml?這不符合OP的要求,因爲它完全忽略了提供的'node'參數。 – JLRishe

+0

@JRRishe所以提出一種不同的方法在SO上是錯誤的?它是如此糟糕,我得到了downvote :) –

+0

不,但提供了一個錯誤的答案是。正如我已經表明的那樣,你的回答並不是OP需要它做的。 – JLRishe

1

如果你沒有結婚到XmlDocument的,並且可以使用Linq2Xml(或者想學習新的東西),另一種方法是:

DotNetFiddle

using System; 
using System.Xml; 
using System.Linq; 
using System.Xml.Linq; 
using System.Collections.Generic; 

public class Program 
{ 
    public static void Main() 
    { 
     //var xDoc = XDocument.Load(filename); 
     var XDoc = XDocument.Parse(@"<root><a><b><c>value</c></b></a><b><c>no</c></b><a><c>no</c></a></root>"); 
     Console.WriteLine("Params a b c "); 
     foreach(var nodeValue in XDoc.Root.GetValueForNodeIfExists("a", "b", "c")) 
     { 
      Console.WriteLine(nodeValue); 
     } 

     Console.WriteLine("List a b c "); 
     foreach(var nodeValue in XDoc.Root.GetValueForNodeIfExists("a", "b", "c")) 
     { 
      Console.WriteLine(nodeValue); 
     } 
    } 
} 


internal static class XElementExtensions 
{ 
    public static IEnumerable<string> GetValueForNodeIfExists(this XElement node, params string[] childNodesNames) 
    { 
     return GetValueForNodeIfExists(node, childNodesNames.ToList()); 
    } 

    public static IEnumerable<string> GetValueForNodeIfExists(this XElement node, IEnumerable<string> childNodesNames) 
    { 
     IEnumerable<XElement> nodes = new List<XElement> { node }; 

     foreach(var name in childNodesNames) 
     { 
      nodes = FilterChildrenByName(nodes, name); 
     } 

     var result = nodes.Select(n => n.Value); 

     return result; 
    } 

    private static IEnumerable<XElement> FilterChildrenByName(IEnumerable<XElement> nodes, string filterName) 
    { 
     var result = nodes 
      .SelectMany(n => n.Elements(filterName)); 

     Console.WriteLine("Filtering by {0}, found {1} elements", filterName, result.Count()); 

     return result;   
    } 
} 

結果:

PARAMS ABC

過濾由,發現2個元素

過濾用b,發現1個元素

過濾通過C,發現1個元素

列表abc

通過a篩選,找到2個元素

過濾用b,發現1個元素

過濾通過C,發現1個元素