使用子列表在 List 中以递归方式读取 XML 树结构

本文关键字:方式 递归 读取 结构 XML List 列表 | 更新日期: 2023-09-27 17:57:49

我有一个这样的XML:

我有一个带有属性名称的成员类。

如何将每个单元及其子单元读取到多个泛型List<Unit>中,这些泛型单元可以使用最新的 .NET 技术以递归方式再次List<Unit>子单元?

<Root>
  <Units Name="Test1">
    <Unit Name="Test11" />
    <Unit Name="Test12">
      <Unit Name="Test21" />
      <Unit Name="Test22" />
      <Unit Name="Test23">
        <Unit Name="Test31" />
        <Unit Name="Test32" />  
        <Unit Name="Test33" />
      </Unit>
      <Unit Name="Test24" />
    </Unit>
  </Units>
  <Units Name="Test2" />
    <!-- ... -->
  <Units Name="Test3" />
    <!-- ... -->
  <Units Name="Test4" />
</Root>

使用子列表<T>在 List<T> 中以递归方式读取 XML 树结构

这将使用普通递归来做到这一点:

public class Unit
{
    public string Name { get; set; }
    public List<Unit> Children { get; set; }
}
class Program
{
    public static void Main()
    {
        XDocument doc = XDocument.Load("test.xml");
        List<Unit> units = LoadUnits(doc.Descendants("Units").Elements("Unit"));
    }
    public static List<Unit> LoadUnits(IEnumerable<XElement> units)
    {
        return units.Select( x=> new Unit() 
                                 { Name = x.Attribute("Name").Value, 
                                   Children = LoadUnits(x.Elements("Unit")) 
                                 }).ToList();
    }
}

挑战在于将其编写为 1 个 LINQ 查询,但这超出了我的范围。LINQ 不容易/不适合递归。

我会勾勒出一个解决方案,我不会把它写出来:

  • 将 Xml 读入 XDocument(或 XmlDocument(
  • 定义class Unit { ... ; ... List<Unit> Children; }
  • 如果需要,定义单元和根类。我将在这里展平该部分
  • 获取所有设备标签的平面列表,var units = doc.Descendants("Unit");
  • 遍历这些元素,我假设父节点总是在嵌套单元之前
  • 查找var Lookup = new Dictionary<XNode, Unit> ();中每个节点的父节点
  • 如果找到父节点,则将当前节点(新单元(添加到其子节点
  • 否则将其添加到顶部列表
  • 将新单元和 XElement 添加到字典中。
  • 只有在创建列表时才需要查找字典。
class Unit
{
    public string Name;
    public List<Unit> Children;
    public Unit(string name, List<Unit> children)
    {
        Name = name;
        Children = children;
    }
    public static Unit Convert(XElement elem)
    {
        return new Unit(elem.Attribute("Name").Value, Convert(elem.Elements()));
    }
    public static List<Unit> Convert(IEnumerable<XElement> elems)
    {
        return elems.Select(Unit.Convert).ToList();
    }
}

你可以像这样使用它:

Unit.Convert(XDocument.Parse(xml).Root.Elements())

为什么不实现一个用于存储单元的树。这将比列表容易和自然得多。

看看这个评论,了解使用 LinkedList 的良好实现。

  • MSDN:算法和数据结构
  • C# 中的树数据结构
  • 树:在 C# 中实现非二叉树

如果你必须使用List,那么你可以使用递归来构建它。我假设您的单位有一个属性(IList Unit.ChildUnits(来保存所有子项列表。如果没有,您可能希望将 Unit 包装到另一个具有此内容的类中。

public List<Unit> LoadAllUnits(XMLNode rootNode){
    List<Unit> allUnits = new List<Unit>();
    foreach(var childNode in rootNode.ChildNodes){
        allUnits.Add(LoadAllSubUnits(childNode);
    }
    return allUnits;
}

private Unit LoadAllSubUnits(XMLNode node){
    Unit u = GetUnitFromCurrentNode(node); // Converts current node into Unit object
    if(root.HasChildNode){
         foreach(var childNode in node.ChildNodes){
             u.ChildUnits.Add(LoadAllSubUnits(childNode);
         }
    }
    return u;
}