0

I'm generating XML file from structs. Some struct members are in List<T> type. I'm using linq queries but I'm stuck at some level.

My structs are like this:

public struct Project
{
    public string name;
    public List<Class> classes;
}

public struct Class
{
    public string name;
    public int class_id;
    public List<Var> vars;
}

public struct Var
{
    public string name;
    public string description;
    public int var_id;
    public List<Element> elements;
}

public struct Element
{
    public string name;
    public string description;
    public int element_id;
    public List<Element> elements;
    public List<Enm> enums;
}

public struct Enm
{
    public string name;
    public int value;
}

and I'm trying to create an XML file that looks like this

<PROJECT name="test project xml">
    <CLASS name="class1" class_id="1">
        <VAR name="var name 1" description="var description" var_id="1">
            <ELEMENT name="element name 1" description="element description" element_id="1"/>

            <ELEMENT name="element name 2" description="element description" element_id="2">
                <ELEMENT name="element name 3" description="element description" element_id="3"/>
            </ELEMENT>

            <ELEMENT name="element name 4" description="element description" element_id="4">
                <ELEMENT name="element name 5" description="element description" element_id="5">
                    <ELEMENT name="element name 6" description="element description" element_id="6"/>
                </ELEMENT>
            </ELEMENT>

            <ELEMENT name="element name 7" description="element description" element_id="7">
                <ENUM name="option 1" value="1"/>
                <ENUM name="option 2" value="2"/>
                <ENUM name="option 3" value="3"/>
            </ELEMENT>
        </VAR>

        <VAR name="var name 2" description="var description" var_id="2">
            <ELEMENT name="element name 8" description="element description" element_id="8">
        </VAR>
    </CLASS>
</PROJECT>

I wrote this code to generate the XML file, but it reaches only the first element level. Elements within elements are skipped. How can I get it to reach lower element levels? Also some element tags have enum tags.

XElement elm = new XElement("PROJECT", new XAttribute("name" project.name),
    project.classes.Select(x=> new XElement("CLASS", new XAttribute("name", x.name), new XAttribute("class_id", x.class_id),
        x.vars.Select(v=> new XElement("VAR", new XAttribute("name", v.name), new XAttribute("description", v.description), new XAttribute("var_id", v.var_id),
            v.elements.Select(e=> new XElement("ELEMENT", new XAttribute("name", e.name),new XAttribute("description", e.description),new XAttribute("element_id", e.element_id)
            )))))));

Is there an easy way to done this with minimum code lines? I think there must be a recursive method to fix my issue.

Cleptus
  • 3,446
  • 4
  • 28
  • 34
lycian
  • 3
  • 1
  • 1
    Do you *really* need that manual serialization? Why not using existing (automatic) serialization methods like `DataContractSerializer` or `XmlSerializer`? – dymanoid Oct 01 '19 at 08:55
  • @dymanoid One of the questions done was in fact "Is there an easy way to done this with minimum code lines?" which your comment addresses. – Cleptus Oct 01 '19 at 09:30
  • Recursion was also the first thing that came te mind to me. This will require to split your code up so you can call a section of it recursively. – Stefan Oct 01 '19 at 11:07

1 Answers1

1

Use Xml Serialization

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

namespace ConsoleApplication132
{
    class Program
    {
        const string FILENAME = @"c:\temp\test.xml";
        static void Main(string[] args)
        {
            XmlWriterSettings settings = new XmlWriterSettings();
            settings.Indent = true;

            XmlWriter writer = XmlWriter.Create(FILENAME, settings);
            XmlSerializer serializer = new XmlSerializer(typeof(Project));
            Project project = new Project();
            serializer.Serialize(writer, project);
        }
    }
    [XmlRoot("PROJECT")]
    public class Project
    {
        [XmlAttribute]
        public string name { get; set; }
        [XmlElement("CLASS")]
        public List<cClass> classes { get; set; }

        public Project()
        {
            name = "test project xml";
            classes = new List<cClass>() {
                new cClass() {
                    name = "class1",
                    class_id = 1,
                    vars = new List<Var>() {
                        new Var() { 
                            name = "var name 1",
                            description = "var description",
                            var_id = 1,
                            elements = new List<Element>() {
                                new Element() {
                                   name = "element name 1",
                                   description = "element description",
                                   element_id = 1
                                },
                                new Element() {
                                   name = "element name 2",
                                   description = "element description",
                                   element_id = 2,
                                   elements = new List<Element>() {
                                       new Element() {
                                           name = "element name 3",
                                           description = "element description",
                                           element_id = 3
                                       }
                                   }
                                },
                                new Element() {
                                   name = "element name 4",
                                   description = "element description",
                                   element_id = 4,
                                   elements = new List<Element>() {
                                        new Element() {
                                           name = "element name 5",
                                           description = "element description",
                                           element_id = 5,
                                           elements = new List<Element>() {
                                               new Element() {
                                                   name = "element name 6",
                                                   description = "element description",
                                                   element_id = 6
                                               }
                                           }
                                        }
                                   }
                                },
                                new Element() {
                                   name = "element name 7",
                                   description = "element description",
                                   element_id = 7,
                                   enums = new List<Enm>() {
                                       new Enm() {
                                          name = "option 1",
                                          value = 1
                                       },
                                       new Enm() {
                                          name = "option 2",
                                          value = 2
                                       },
                                       new Enm() {
                                          name = "option 3",
                                          value = 3
                                       }
                                   }
                                }
                            }
                        },
                        new Var() { 
                            name = "var name 2",
                            description = "var description",
                            var_id = 2,
                            elements = new List<Element>() {
                                new Element() {
                                   name = "element name 8",
                                   description = "element description",
                                   element_id = 8
                                }
                            }
                        }
                   }
                }
            };

        }
    }

    public class cClass
    {
        [XmlAttribute]
        public string name { get; set; }
        [XmlAttribute]
        public int class_id;
        [XmlElement("VAR")]
        public List<Var> vars;
    }

    public class Var
    {
        [XmlAttribute]
        public string name;
        [XmlAttribute]
        public string description;
        [XmlAttribute]
        public int var_id;
        [XmlElement("ELEMENT")]
        public List<Element> elements;
    }

    public struct Element
    {
        [XmlAttribute]
        public string name;
        [XmlAttribute]
        public string description;
        [XmlAttribute]
        public int element_id;
        [XmlElement("ELEMENT")]
        public List<Element> elements;
        [XmlElement("EMNU")]
        public List<Enm> enums;
    }

    public struct Enm
    {
        [XmlAttribute]
        public string name;
        [XmlAttribute]
        public int value;
    }

}
jdweng
  • 33,250
  • 2
  • 15
  • 20
  • I forgot to say that the number and depth of element tags can change dynamically according to source files. Also number of clas and var tags can change too. It's a totally dynamic structure. From the example you gave, you created the project manually. I don't know much about XmlSerializer. Can your example works with the dynnamic structure which I mentioned? Also can [XmlTags] works with struct members? – lycian Oct 01 '19 at 11:51
  • The tags need to be predefined they cannot be dynamic, but having unused (null) class properties is acceptable. The only way to be fully dynamic is to use Reflection (PropertyInfo) to enumerate through the classes. – jdweng Oct 01 '19 at 12:40
  • It is not the answer of my question but I changed my method to serialization. – lycian Oct 03 '19 at 11:42
  • With Xml Linq you would of needed a recursive algorithm. The serialization method was simpler in this case. – jdweng Oct 03 '19 at 12:00