2012-01-09 3 views
4

Хорошо, у меня есть следующий XML-деревоИспользование Linq To XML, способ получить путь ко всем листьям?

<root> 
    <A> 
     <A1> 
      <A1A>1000</A1A> 
      <A1B>2000</A1B> 
      <A1C>3000</A1C> 
     </A1> 
     <A2> 
      <A2A>4000</A2A> 
      <A2B>5000</A2B> 
     </A2> 
    </A> 
    <B> 
     <B1> 
      <B1A>6000</B1A> 
     </B1> 
    </B> 
</root> 

От способа приема в XDocument я хочу, чтобы произвести словарь, где ключ является путь (действительно XPath) и значение происходит от значения в соответствующий лист.

root/A/A1/A1A 1000 
root/A/A1/A1B 2000 
root/A/A1/A1C 3000 
root/A/A2/A2A 4000 
root/A/A2/A2B 5000 
root/B/B1/B1A 6000 

Кажется, что это просто сделать в Linq для XML, но я не могу обернуть вокруг него голову.

ответ

8

Вы можете найти листья, ища элементы, которые не имеют потомков:

var doc = XDocument.Load(fileName); 
var leaves = 
    from e in doc.Descendants() 
    where !e.Elements().Any() 
    select e; 

Я не знаю, если есть встроенный способ получить путь элемента, но вы можете легко создать метод расширения, чтобы построить его:

static class Extensions 
{ 
    public static string Path(this XElement element) 
    { 
     XElement tmp = element; 
     string path = string.Empty; 
     while (tmp != null) 
     { 
      path = "/" + tmp.Name + path; 
      tmp = tmp.Parent; 
     } 
     return path; 
    } 
} 

Вы можете создать словарь, как это:

var dict = leaves.ToDictionary(e => e.Path(), e => e.Value); 
+0

Осторожнее с индексами элементов, проверить этот вопрос: http://stackoverflow.com/questions/451950/get-the-xpath-to-an-xelement – Alpha

+0

Я был так глубоко в поиске пути к сделайте это в LinqToXml, я полностью упустил из виду, насколько просто просто построить путь, используя родителей ... doh! Я оставлю вопрос открытым на некоторое время, если кто-то знает, как это сделать в LinqToXml. Большое спасибо. –

2

После разбора XML на XDocument, который, как я полагаю, вы уже успели сделать, используйте приведенные ниже методы. Обратите внимание, что реализация GetPath() довольно проста. См. this answer для лучшей реализации.

public Dictionary<string, string> GetLeaves(XDocument doc) 
{ 
    var dict = doc 
     .Descendants() 
     .Where(e => !e.HasElements) 
     .ToDictionary(e => GetPath(e), e.Value); 

    return dict; 
} 

private string GetPath(XElement element) 
{ 
    var nodes = new List<string>(); 
    var node = element; 
    while (node != null) 
    { 
     nodes.Add(node.Name.ToString()); 
     node = node.Parent; 
    } 

    return string.Join("/", Enumerable.Reverse(nodes)); 
} 
+0

Я не вижу необходимости использовать расширение, как это сделал другой ответ. Для меня этот ответ выглядит более элегантным, читабельным и удобным. – daniloquio