根据XML属性自动加载来自直接父项的子项

时间:2013-08-22 10:06:24

标签: c# xml linq

我有一个XML文件,看起来像......

<Modules>
    <Module Title='Mod1' Attr2='' Attr3=''>
        <Functions>
            <Function Title='Fun1' Attr2=''>
                <SubFunctions>
                    <SubFunction Title='SubFun1' Attr2='' />
                    <SubFunction Title='SubFun2' Attr2='' />
                </SubFunctions>
            </Function>
            <Function Title='Fun2' Attr2='' />
            <Function Title='Fun3' Attr2='' />
         </Functions>
</Module>
<Module Title='Mod2' Attr2='' Attr3=''>
    <Functions>
    </Functions>
</Module>
<Module Title='Mod3' Attr2='' Attr3=''>
    <Functions>
    </Functions>
</Module>

我一直在尝试使用C#中的LINQ和XML编写一个基于递归的泛型函数,但是却无法使函数尽可能地通用。

我目前所拥有的功能是加载与“模块”相关的所有数据,因此根据上述XML数据计数为3。当用户选择'Mod1'作为第一个“模块”时,它应该加载在该'Mod1'下的所有相应数据,即Fun1,Fun2,Fun3。再次,如果用户选择Fun1,它应该加载此'Fun1'下的所有数据,即SubFun1,SubFun2等。

请注意:我不想在C#应用程序中对任何XML标记进行硬编码。

提前致谢。

1 个答案:

答案 0 :(得分:0)

如果您不想硬编码标签的名称,我们应该对XML结构做出假设。根据你的例子,似乎有两种元素:

  1. “容器”元素。模块,函数,子函数。哪个可以包含零个或多个“数据”元素
  2. “数据”元素。模块,功能,子功能。其中可以包含零个或一个“Container”元素,并且始终具有用于选择的“Title”属性。
  3. 请注意,除非您使用自定义名称,否则“标题”属性名称是硬编码的。你的决定。

    代码可以是通用的,因为我们不关心元素的名称,而只关心它们的关系。 如您所述,用户逐渐选择所选元素,遵循此要求我将提出以下解决方案:

    MyXmlReader - 提供对XML文件的访问的类 DataElement - 包含有关查询元素的数据的类,包括其子元素

     class MyXmlReader
    {
        // refference to current list element
        XmlNode currentListElement = null;
        XmlDocument xml;
    
        // load file, initialize  and return data that contains info ablut
        // the first level elements names
        public DataElement Load(string path)
        {
            xml = new XmlDocument();
            xml.Load(path);
            Init();
            DataElement result = new DataElement();
            result.SubTitles = GetChildTitles();
            return result;
        }
    
        // Initialize the reader to read from the beggining
        public void Init()
        {
            currentListElement = xml.DocumentElement;
        }
    
        // Get next child
        public DataElement GetNext(string title)
        {
            string tempTitle;
    
            foreach (XmlNode child in currentListElement.ChildNodes)
            {
                DataElement result = null;
    
                if (child.Attributes["Title"].Value == title)
                {
                    // create object that contains the data about the child nodes Titles
                    result = new DataElement();
                    result.Title = child.Attributes["Title"].Value;
    
                    if (child.FirstChild != null) // means no child nodes
                    {
                        currentListElement = child.FirstChild;
    
                        // add subelements subtitles
                        result.SubTitles.AddRange(GetChildTitles());
                    }
                    return result;
    
    
                }
            }
            return null;
        }
    
    
        public List<string> GetChildTitles()
        {
            List<string> result = new List<string>();
    
            foreach (XmlNode child in currentListElement.ChildNodes)
            {
                result.Add(child.Attributes["Title"].Value);
            }
            return result;
        }
    
    
    }
    
    
    // add any other data to this class
    // that you need about the element you return
    class DataElement
    {
        public List<string> SubTitles = new List<string>();
        public string Title { get; set; }
    }
    

    测试:

    // test
        static void Main(string[] args)
        {
            MyXmlReader reader = new MyXmlReader();
            DataElement data = reader.Load("Data.xml");
    
            // Generic test:
    
            // get first module
            data = reader.GetNext(data.SubTitles[0]);
            // get first function 
            data = reader.GetNext(data.SubTitles[0]);
            // get first sub-function 
            data = reader.GetNext(data.SubTitles[0]);         
    
    
            // you can write test with hardcode nodes names like this:
            reader.Init();
            // get first module
            data = reader.GetNext("Mod1");
            // get first function 
            data = reader.GetNext("Fun1");
            // get first sub-function 
            data = reader.GetNext("SubFun1");  
    
            Console.Read();
        }