Deserialize Xml to Object Using Dynamic

Deserialize XML To Object using Dynamic

You may want to try this.

string xml = @"<Students>
<Student ID=""100"">
<Name>Arul</Name>
<Mark>90</Mark>
</Student>
<Student>
<Name>Arul2</Name>
<Mark>80</Mark>
</Student>
</Students>";

dynamic students = DynamicXml.Parse(xml);

var id = students.Student[0].ID;
var name1 = students.Student[1].Name;

foreach(var std in students.Student)
{
Console.WriteLine(std.Mark);
}

public class DynamicXml : DynamicObject
{
XElement _root;
private DynamicXml(XElement root)
{
_root = root;
}

public static DynamicXml Parse(string xmlString)
{
return new DynamicXml(XDocument.Parse(xmlString).Root);
}

public static DynamicXml Load(string filename)
{
return new DynamicXml(XDocument.Load(filename).Root);
}

public override bool TryGetMember(GetMemberBinder binder, out object result)
{
result = null;

var att = _root.Attribute(binder.Name);
if (att != null)
{
result = att.Value;
return true;
}

var nodes = _root.Elements(binder.Name);
if (nodes.Count() > 1)
{
result = nodes.Select(n => n.HasElements ? (object)new DynamicXml(n) : n.Value).ToList();
return true;
}

var node = _root.Element(binder.Name);
if (node != null)
{
result = node.HasElements || node.HasAttributes ? (object)new DynamicXml(node) : node.Value;
return true;
}

return true;
}
}

--EDIT--

To make it work with xml namespaces, I added RemoveNamespaces method.

public class DynamicXml : DynamicObject
{
XElement _root;
private DynamicXml(XElement root)
{
_root = root;
}

public static DynamicXml Parse(string xmlString)
{
return new DynamicXml(RemoveNamespaces(XDocument.Parse(xmlString).Root));
}

public static DynamicXml Load(string filename)
{
return new DynamicXml(RemoveNamespaces(XDocument.Load(filename).Root));
}

private static XElement RemoveNamespaces(XElement xElem)
{
var attrs = xElem.Attributes()
.Where(a => !a.IsNamespaceDeclaration)
.Select(a => new XAttribute(a.Name.LocalName, a.Value))
.ToList();

if (!xElem.HasElements)
{
XElement xElement = new XElement(xElem.Name.LocalName, attrs);
xElement.Value = xElem.Value;
return xElement;
}

var newXElem = new XElement(xElem.Name.LocalName, xElem.Elements().Select(e => RemoveNamespaces(e)));
newXElem.Add(attrs);
return newXElem;
}

public override bool TryGetMember(GetMemberBinder binder, out object result)
{
result = null;

var att = _root.Attribute(binder.Name);
if (att != null)
{
result = att.Value;
return true;
}

var nodes = _root.Elements(binder.Name);
if (nodes.Count() > 1)
{
result = nodes.Select(n => n.HasElements ? (object)new DynamicXml(n) : n.Value).ToList();
return true;
}

var node = _root.Element(binder.Name);
if (node != null)
{
result = node.HasElements || node.HasAttributes ? (object)new DynamicXml(node) : node.Value;
return true;
}

return true;
}
}

Deserialize XML into object with dynamic child elements

I've managed to resolve this using a dynamic type when deserializing.
When I deserialize ValuesRead, it is a defined as a dynamic type.

When deserialized, it turns into an XmlNode and from there I iterate over the node use the Name and InnerText values to read all the data.

Deserialize dynamic XML

Since you don't know what elements might be present in your Hit class, you can add a List<XElement> property to you class and attach the [XmlAnyElement] attribute to it. It will then capture any and all unknown elements in the XML for the class. Once the elements are deserialized, you can add API properties to query for elements with specific names, for instance:

public class Hit
{
// Since "id" is expected in the XML, deserialize it explicitly.
[XmlElement("id")]
public string Id { get; set; }

private readonly List<XElement> elements = new List<XElement>();

[XmlAnyElement]
public List<XElement> Elements { get { return elements; } }

#region convenience methods

public string this[XName name]
{
get
{
return Elements.Where(e => e.Name == name).Select(e => e.Value).FirstOrDefault();
}
set
{
var element = Elements.Where(e => e.Name == name).FirstOrDefault();
if (element == null)
Elements.Add(element = new XElement(name));
element.Value = value;
}
}

const string title = "Title";

[XmlIgnore]
public string Title
{
get
{
return this[title];
}
set
{
this[title] = value;
}
}

#endregion
}

Incidentally, you can eliminate your Hits class if you mark the Hits array with [XmlArray] rather than [XmlElement], like so:

[XmlRoot("SearchResponse")]
public sealed class SearchResponse
{
[XmlElement("Response", Type = typeof(Response))]
public Response[] Responses { get; set; }

[XmlElement("HitsCount", Type = typeof(HitsCount))]
public HitsCount[] HitsCount { get; set; }

[XmlArray("Hits")] // Indicates that the hits will be serialized with an outer container element named "Hits".
[XmlArrayItem("Hit")] // Indicates that each inner entry element will be named "Hit".
public Hit [] Hits { get; set; }
}

Update

The

    public string this[XName name] { get; set; }

Is an indexer. See Using Indexers (C# Programming Guide). I added it so it would be easy to do things like:

var description = hit["Description"];
var title = hit["Title"];

The indexer looks for the first XML element with the specified name, and returns its text value. If you don't want it, you can leave it out -- it's just for convenience.

Deserialize dynamic XML with generics C#

With Response<SoccerRoot>, the expected xml layout would be:

<RESPONSE>
<Data>
<SOCCER AnotherAttribute=...>

which does not match what you have. There is no way of customizing the element name of the T Data just using attributes.

Options:

  • have multiple root objects (one each for SOCCER, RESULTS, etc):

    [XmlRoot("RESPONSE")]
    public class SoccerRoot {
    [XmlElement("SOCCER")]
    public Soccer Soccer {get;set;}
    }
    [XmlRoot("RESPONSE")]
    public class ResultsRoot {
    [XmlElement("RESULTS")]
    public Results Results {get;set;}
    }
  • have a single root object that has multiple first-level children (one for SOCCER, one for RESULTS, etc - each of one type

    [XmlRoot("RESPONSE")]
    public class Response {
    [XmlElement("RESULTS")]
    public Results Results {get;set;}
    [XmlElement("SOCCER")]
    public Soccer Soccer {get;set;}
    }
  • use the generics approach, but lose the intermediate object so you have Response<Soccer> (delete SoccerRoot completely), and use XmlAttributeOverrides to customize the element name, being sure to cache the XmlSerializer generated (otherwise it will leak at an alarming rate)

Frankly, I'd go with either of the first two options and leave the third one alone.

Generic XML Deserialization into Undefined Objects

You're looking for an ExpandoObject.

ExpandoObject is a dynamic object introduced in C#4.

Sample implementation found here:

 public static IEnumerable<dynamic> GetExpandoFromXml(string file, string descendantid)
{
var expandoFromXml = new List<dynamic>();

var doc = XDocument.Load(file);
var nodes = doc.Root.Descendants(descendantid);

foreach (var element in doc.Root.Descendants(descendantid))
{
dynamic expandoObject = new ExpandoObject();
var dictionary = expandoObject as IDictionary<string, object>;
foreach (var child in element.Descendants())
{
if (child.Name.Namespace == "")
dictionary[child.Name.ToString()] = child.Value.Trim();
}
yield return expandoObject;
}
}

More links:

http://www.codeproject.com/Tips/227139/Converting-XML-to-an-dynamic-object-using-ExpandoO

http://www.codeproject.com/Articles/461677/Creating-a-dynamic-object-from-XML-using-ExpandoOb

How to get deserialized xml attribute from dynamic object

To deal with special characters, such as "@" in dynamic object, you must cast it to `
(IDictionary). And then you can get the recevied attribute as bellow:

var received = ((IDictionary<string, object>)obj.Message)["@recevied"];

How to serialize dynamic object to xml c#

You can implement your own serialize object by using IXmlSerializable

[Serializable]
public class ObjectSerialize : IXmlSerializable
{
public List<object> ObjectList { get; set; }

public XmlSchema GetSchema()
{
return new XmlSchema();
}

public void ReadXml(XmlReader reader)
{

}

public void WriteXml(XmlWriter writer)
{
foreach (var obj in ObjectList)
{
//Provide elements for object item
writer.WriteStartElement("Object");
var properties = obj.GetType().GetProperties();
foreach (var propertyInfo in properties)
{
//Provide elements for per property
writer.WriteElementString(propertyInfo.Name, propertyInfo.GetValue(obj).ToString());
}
writer.WriteEndElement();
}
}
}

Usage;

        var output = new List<object>
{
new { Sample = "Sample" }
};
var objectSerialize = new ObjectSerialize
{
ObjectList = output
};
XmlSerializer xsSubmit = new XmlSerializer(typeof(ObjectSerialize));
var xml = "";

using (var sww = new StringWriter())
{
using (XmlWriter writers = XmlWriter.Create(sww))
{
try
{
xsSubmit.Serialize(writers, objectSerialize);
}
catch (Exception ex)
{

throw;
}
xml = sww.ToString(); // Your XML
}
}

Output

<?xml version="1.0" encoding="utf-16"?>
<ObjectSerialize>
<Object>
<Sample>Sample</Sample>
</Object>
</ObjectSerialize>

Note : Be careful with that, if you want to deserialize with same type
(ObjectSerialize) you should provide ReadXml. And if you want to
specify schema, you should provide GetSchema too.



Related Topics



Leave a reply



Submit