1

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?

6
  • Why don't you want to go with Serialization? Commented Nov 9, 2012 at 12:13
  • I would not go with recursion, stack overflow possibility too high on complex data sets Commented Nov 9, 2012 at 12:14
  • Serialization is a good idea, yes, but the problem is that I am having trouble wrapping my head around children objects. Performance of this application is also not a concern. Commented Nov 9, 2012 at 12:18
  • I think your making this more difficult for yourself, but if its what your comfortable with then that's down to you. Commented Nov 9, 2012 at 12:19
  • Its totally possible I am making this hard for myself. This XML will get turned into an XSD, then We use this XSD in java along with XJC binding files and Ant build to create Java Classes for us. along with that, We have invented a Code Generator application to turn an XSD file into an Upsert method which will be called upon a Google Webservice to add/update data in their DFP and DFA API Commented Nov 9, 2012 at 12:23

2 Answers 2

1

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>();
    }

}
Sign up to request clarification or add additional context in comments.

3 Comments

Having trouble figuring out what I should define TableItems as. I am using a datagridview to read in the data
@Eon Rusted du Plessis, you can define it as: var items = dataGridView.Rows.Select(r => ParseXmlConstruct(r)); where ParseXmlConstruct converts row to XmlConstruct
@2kay - This is a great and clean solution. I gave you an up vote. It is not trivial and straight-forward to figure out it. I modified some codes to include auto default constructor for class Node to avoid null reference for Node's Children collection when Node class is instantiated.
0

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 + "  ");
}

4 Comments

proof of concept is all i need :) Going to try this out. The Gridview should never grow past 30 rows in size though...
So I haven't tried your code yet, been thinking on yours. This algorithm of yours, wont it cause the end result to contain all elements, with the child elements? I think this one may turn out weird :)
Just wondering, won't this cause duplicated objects in the object tree being constructed?
@EonRustedduPlessis: no, I don't reckon this will make any duplicate objects. I added an example that would output the tree with indentation.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.