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 forRESULTS
, 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>
(deleteSoccerRoot
completely), and useXmlAttributeOverrides
to customize the element name, being sure to cache theXmlSerializer
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 provideReadXml
. And if you want to
specify schema, you should provideGetSchema
too.
Related Topics
How to Get Variable Name Using Reflection
Unity Singleton Manager Classes
Is the Use of Dynamic Considered a Bad Practice
Why Are Unsigned Int's Not Cls Compliant
Accessing Password Protected Network Drives in Windows in C#
Value of Type 'T' Cannot Be Converted To
No Overflow Exception for Int in C#
C# "Parameter Is Not Valid." Creating New Bitmap
Does C# Optimize the Concatenation of String Literals
How to Parse Hex Values into a Uint
Read from Location on Console C#
Why Must I Provide Explicitly Generic Parameter Types While the Compiler Should Infer the Type
How to Fix a Opacity Bug with Drawtobitmap on Webbrowser Control
Why Do These Division Equations Result in Zero
How to Format a Datetime in a Different Format