JavaSa
JavaSa

Reputation: 6242

Building Xml with XElement dynamically through recursion

I'm new to linq to Xml.
I have a recursive method which get as parameter XElement root which should hold the XML data in a way it will represent the correlating subtree root of a given recursion depth.

void recursiveMethod(XElement root);
To be more specific,also look at this XML example:

<start>
      <Class>
           <Worker>
                <Name> Dan </Name>
                <Phone> 123 </Phone> 
                <Class>
                      <Address>
                           <Street> yellow brick road </Street>
                           <Zip Code> 123456 </Zip Code>
                      </Address>
                </Class>
            </Worker>
      </Class>
...
</start>    

As you can imagine, Name is value type whereas Address is a class reference.
The Xml information should be added dynamically through reflection ( in top down approach).

To make the long story short imagine that I'm in the middle of investigating Worker Class and reached Address Class and want to "drill down", so I want to call my recursive method with the right reference of child nodes of the current Worker class as the new XElement root, so I will be able to add to it what I found by reflection in the Address Class one recursion depth below.

Note that this reference should be of XElement type.

How can I do that?

EDIT: If you have another idea of doing all this stuff but not with XElement I'll be happy to hear about also, although I prefer it with XElement parameter.

Another issue:
I've started implementing it in a naive way like iterating through all fields (variable of FieldInfo[]), and if I had encounterd value type(IsValueType) I was doing something like

 root.Add(new XElement("Field",
                      new XElement("Type", ...),
                      new XElement("Variable Name", ...),
                      new XElement("Value", ...)));     

So ,just for general knowledge:
1. Was there a way to get only the reference of a node to its decendants ,so that in lower recursion level I'll be able to do another root.Add(...) as above but this root will be a reference to children of previous root? (Which means doing the whole operation without Linq syntax)

2.I've managed to get private fields value through reflection without working with properties, is it problematic? Should I always take values through properties in reflection?

Upvotes: 3

Views: 3169

Answers (1)

Sergey Berezovskiy
Sergey Berezovskiy

Reputation: 236318

This extension method will build XElement in required format:

public static class Extensions
{
    public static XElement ToXml<T>(this T obj)
    {
        Type type = typeof(T);

        return new XElement("Class",
                    new XElement(type.Name,
                        from pi in type.GetProperties()
                        where !pi.GetIndexParameters().Any()
                        let value = (dynamic)pi.GetValue(obj, null)
                        select pi.PropertyType.IsPrimitive || 
                               pi.PropertyType == typeof(string) ?
                                new XElement(pi.Name, value) : 
                                Extensions.ToXml(value)
                        )
                    );
    }
}

What happens here:

  • We get public properties of passed object (you can add BindingFlags to filter properties).
  • Next I verify if property has index parameters and skip such properties.
  • Next I get property value as dynamic object. It's important, otherwise property value type will be inferred as object during recursive call of ToXml<T> method.
  • I check if property type is primitive type (int, byte, long, single, double, etc) or string
  • For primitive types we write element with property value. For other types (complex) we recursively start building XElement

Usage:

Worker worker = new Worker()
{
    Name = "Serge",
    Phone = "911",
    Address = new Address() { Street = "Elm street", ZipCode = 666 }
};

XElement xml = worker.ToXml();

Result:

<Class>
  <Worker>
    <Name>Serge</Name>
    <Phone>911</Phone>
    <Class>
      <Address>
        <Street>Elm street</Street>
        <ZipCode>666</ZipCode>
      </Address>
    </Class>
  </Worker>
</Class>

BUT you should be careful with situations when two objects refer each other (infinite recursion will happen in this case)

In this case you can use something like dictionary or hashset to store all objects which already exist in your xml:

Type type = obj.GetType();
if (set.Contains(obj))
    return new XElement("Class", new XAttribute("name", type.Name));
set.Add(obj);
return new XElement("Class", ...);

Upvotes: 5

Related Questions