Eon
Eon

Reputation: 3974

How can I build an Hierarchy of data in C# using a table of data

I am in a middle of an exercise where I am trying to manually make an XML file from data which a user enters into a table. Here is a sample Table:

Note, in my code example later, more fields are exposed than this table for future purposes.

Note 2, The data in this table is manually entered by a user into a datagridview control on my windows form application. No SQL involved here.

ID | ParentID | Element | DefaultValue |
========================================
0  | -1       | root    |              |
1  | 0        | first   | SomeData     |
2  | 0        | second  | OtherData    |
3  | 0        | third   |              |
4  | 3        | firstCh | Child of ID=3| 
========================================

I am trying to construct an XML File from this using C#, and without using any XML Classes. I am trying to do this through pure string manipulation and formatting, because if this test succeeds, I may expand it to do more things which could prove use to us in our production environment at work.

My current thought pattern is to iterate through each and every element, determine if the element at a certain ID contains a child, build a object list out of these children. Iterate through the child list, then determining if "grandchildren" exist in the children elements in the children list. Here is my code class which I made.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;


namespace XMLtoXSDconverter
{
    class XMLConstruct
    {
        public enum ElementType { None, StringElement, LongElement, BoolElement, DateTimeElement, FloatElement, IntElement, DoubleElement };
        public int id;
        public int parentID;
        public string element;
        public string comment;
        public int minOccurs;
        public int maxOccurs;
        List<XMLConstruct> children;
        int indent = 0;

        public string buildXMLConstruct(int id, int parentId, string element, string comment, ElementType elementType, List<XMLConstruct> children, int minOccurs, int maxOccurs)
        {
            this.id = id;
            this.parentID = parentId;
            this.element = element;
            this.comment = comment;
            this.minOccurs = minOccurs;
            this.maxOccurs = maxOccurs;

            string elementTypeString = string.Empty;
            switch (elementType)
            {
                case ElementType.StringElement: elementTypeString = "xs:string"; break;
                case ElementType.LongElement: elementTypeString = "xs:long"; break;
                case ElementType.IntElement: elementTypeString = "xs:int"; break;
                case ElementType.FloatElement: elementTypeString = "xs:float"; break;
                case ElementType.DoubleElement: elementTypeString = "xs:double"; break;
                case ElementType.DateTimeElement: elementTypeString = "xs:dateTime"; break;
                case ElementType.BoolElement: elementTypeString = "xs:boolean"; break;
                default: elementTypeString = string.Empty; break;
            }

            if (this.id == 0)
                element += "Wrapper";

            //Hiccup here, how would I construct the children elements? Recursion perhaps? These children should come in the List<XSDConstruct>, which may contain children too
            return null;
        }
    }
}

I have been scratching my head about how to approach this problem and how to solve it. As you can see, the reader and my possible savior from this Friday afternoon nightmare, XSDConstruct contains most fields required to be able to make an XML and XSD file out of this. There is a List<XSDConstruct> child object which can contain the children of each element, and so forth, clearly yelling at using recursion.

So my question is, how will I read the data in my table into my object to contain a tree of objects, n - levels deep?

Upvotes: 1

Views: 1449

Answers (2)

tukaef
tukaef

Reputation: 9214

Hope this help:

Node MakeTree()
{   
    // create root, build subtree and return it
    var node = new Node() {Id = 0, ParentId = -1};
    MakeSubTree(node);
    return node;
}
void MakeSubTree(Node parentNode)
{
    // find all children of parent node (they have parentId = id of parent node)
    var nodes = TableItems.Where(e => e.ParentId == parentNode.Id)
                    .Select(e => new Node {ParentId = e.ParentId, Id = e.Id});

    // build subtree for each child and add it in parent's children collection
    foreach (var node in nodes)
    {
        MakeSubTree(node);
        parentNode.Children.Add(node);             
    }
}

TableItems is collection of your items. Class Node is defined as:

public class Node
{
    public int Id { get; set; }
    public int ParentId { get; set; }
    public List<Node> Children { get; set; } 

    public Node()
    {
        Children = new List<Node>();
    }

}

Upvotes: 1

Christoffer
Christoffer

Reputation: 2125

First thing that comes to mind is to handle the children outside the constructor of XMLConstruct. You should be able to build a lookup table like this:

var lookupTable = new Dictionary<int, XMLConstruct>();
// note: this is probably not how you iterate over gridview rows,
// just making a proof of concept:
foreach(var row in gridView)
{
    var xc = GetXmlConstruct(row); // build the construct from the gridvew row
    lookupTable.Add(xc.id, xc);
}

Now that you have all the constructs, you can do a pass over all constructs and assign the children:

foreach(var xc in lookupTable.Values)
{
    XMLConstruct parent;
    if (lookupTable.TryGetValue(xc.parentID, out parent))
        parent.children.Add(xc);
}

It's important that the children property is initialized, otherwise you will get a null pointer exception.

It is now possible to traverse the tree like so:

// assuming that the element with id=0 is the root element
var rootElement = lookupTable[0];
Traverse(rootElement, "");

// remember, this is just a proof of concept, 
// you probably need to render opening/ending tags etc.
public Traverse(XMLConstruct xc, string indentation)
{
    Console.WriteLine(indentation + xc.Element);
    foreach(var child in xc.Children)
        Traverse(child, indentation + "  ");
}

Upvotes: 0

Related Questions