Deserialize XML to an Object. BUT you don’t know which object to use?

I

ink

Hi all,

I would like to be able to select the object used to Deserialize an XML
string based on the XML itself.
I need to do this because the XML is being read from a database and there
are a number of different objects. The XML is being inserted into the
database as a log of all trafice between systems and I have been asked to
write an app that will display the trafic in a human readable form. Want to
use reflection to get the actual object names. For Example.

Given the following XML:

<TR CST="2008-08-28T12:15:34+01:00" SRT="2008-08-28T12:15:40.12232+01:00" />

How can I figger out which object I need to use to Deserialize. This must
be at run time.

[Serializable]
[XmlType("TR")]
public class TimeSyncResponseMessage
{
[XmlAttribute("CST")]
public DateTime ClientSendTime;

[XmlAttribute("SRT")]
public DateTime ServerResponseTime;
}

[Serializable]
[XmlType("TS")]
public class TimeSyncRequestMessage
{
[XmlAttribute("T")]
public DateTime SendTime;
}


Some of the Rules would be:

1. The object them selves can not change as thes are being used in a live
system.
2. I can put them into another class like a parent if needed.

I was thinking that maybe I could look at the XML tag “TR†and then using
reflection loop through the members comparing the [XmlType("TR")] till I
found the correct object. The root tags are all unique.

But I don’t know how to do this? or if it is even possible.

Any sugestions?

Thanks.
Ink
 
B

Bjørn Brox

ink skrev:
Hi all,

I would like to be able to select the object used to Deserialize an XML
string based on the XML itself.
I need to do this because the XML is being read from a database and
there are a number of different objects. The XML is being inserted into
the database as a log of all trafice between systems and I have been
asked to write an app that will display the trafic in a human readable
form. Want to use reflection to get the actual object names. For Example.

Given the following XML:

<TR CST="2008-08-28T12:15:34+01:00"
SRT="2008-08-28T12:15:40.12232+01:00" />

How can I figger out which object I need to use to Deserialize. This
must be at run time.

[Serializable]
[XmlType("TR")]
public class TimeSyncResponseMessage
{
[XmlAttribute("CST")]
public DateTime ClientSendTime;

[XmlAttribute("SRT")]
public DateTime ServerResponseTime;
}

[Serializable]
[XmlType("TS")]
public class TimeSyncRequestMessage
{
[XmlAttribute("T")]
public DateTime SendTime;
}


Some of the Rules would be:

1. The object them selves can not change as thes are being used in a
live system.
2. I can put them into another class like a parent if needed.

I was thinking that maybe I could look at the XML tag “TR†and then
using reflection loop through the members comparing the [XmlType("TR")]
till I found the correct object. The root tags are all unique.

But I don’t know how to do this? or if it is even possible.

Any sugestions?

Thanks.
Ink
The matching root element of the XML file of course.
An xml file without a root element is not a legal xml file.
 
M

Martin Honnen

ink said:
I was thinking that maybe I could look at the XML tag “TR†and then
using reflection loop through the members comparing the [XmlType("TR")]
till I found the correct object. The root tags are all unique.

But I don’t know how to do this? or if it is even possible.

Well use an XML API like XmlReader to read out the LocalName of the root
element. A for the reflection part, read
http://msdn.microsoft.com/en-us/library/a4a92379.aspx or the
corresponding section in your local MSDN copy.
 
I

ink

Thanks for you input guys, but that is the part that i already have. see
below.
I am sorry i didn't explane myself more clearly.

The problem is that i can not tell if the objects have values in them and
how to convert the Text name into an object it self.
i can list out all the Names but how do i use a name to load an object type
and test if it is NULL or not?

Every message has a root "<TM>" this is something that i have added during
processing. and the parent class MessageTypes is also something i have
added.



private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{

MemoryStream myStream = new
MemoryStream(Encoding.UTF8.GetBytes(listBox1.SelectedItem.ToString()));

using (myStream)
{
using (XmlTextReader xr = new XmlTextReader(myStream))
{
xr.Read();

xr.MoveToContent();

MessageTypes commsData = (MessageTypes)XmlToObject(xr,
typeof(MessageTypes));

Type objectType = commsData.GetType();

// get all the Nested Types
foreach (Type mt in objectType.GetNestedTypes())
{
listBox2.Items.Add(mt.Name);

foreach (object oAtt in
mt.GetCustomAttributes(true))
{
if (typeof(XmlTypeAttribute) == oAtt.GetType())
{
XmlTypeAttribute xa =
(XmlTypeAttribute)oAtt;
listBox2.Items.Add(xa.TypeName);
}
}
}

}
}

}






[Serializable]
[XmlType("MT")]
public class MessageTypes
{
[Serializable]
[XmlType("TR")]
public class TimeSyncResponseMessage
{
[XmlAttribute("CST")]
public DateTime ClientSendTime;

[XmlAttribute("SRT")]
public DateTime ServerResponseTime;
}

[Serializable]
[XmlType("TS")]
public class TimeSyncRequestMessage
{
[XmlAttribute("T")]
public DateTime SendTime;
}


}


And this is the XML.

<TM><TR CST="2008-08-28T12:15:34+01:00"
SRT="2008-08-28T12:15:40.12232+01:00" /></TM>


Thanks
ink


















Martin Honnen said:
ink said:
I was thinking that maybe I could look at the XML tag “TR†and then using
reflection loop through the members comparing the [XmlType("TR")] till I
found the correct object. The root tags are all unique.

But I don’t know how to do this? or if it is even possible.

Well use an XML API like XmlReader to read out the LocalName of the root
element. A for the reflection part, read
http://msdn.microsoft.com/en-us/library/a4a92379.aspx or the corresponding
section in your local MSDN copy.
 
B

Bjørn Brox

ink skrev:
Thanks for you input guys, but that is the part that i already have. see
below.
I am sorry i didn't explane myself more clearly.

The problem is that i can not tell if the objects have values in them
and how to convert the Text name into an object it self.
i can list out all the Names but how do i use a name to load an object
type and test if it is NULL or not?

Every message has a root "<TM>" this is something that i have added
during processing. and the parent class MessageTypes is also something i
have added.



private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{

MemoryStream myStream = new
MemoryStream(Encoding.UTF8.GetBytes(listBox1.SelectedItem.ToString()));

using (myStream)
{
using (XmlTextReader xr = new XmlTextReader(myStream))
{
xr.Read();
....
I thought you said you should Deserialize XML?

This is manually reading a xml file...
Have you ever tried something like:

using System.Xml.Serialization;
....
XmlSerializer xsz = new XmlSerializer(typeof(MessageTypes));
MessageTypes mt = xsz.Deserialize(myStream);
 
I

ink

Hi,

Thanks for your input.

That is in fact how i am doing it. I only use the XmlTextReader to get the
LocalName so that i can distinguish what object type to pass to the
XmlSerializer programmatically.

ink
 
I

ink

In case somebody in a hunder years from now wants to know how i solved this
problem here it is.

BE warned i have not cleand this code up yet so there are still variables
and alsorts floting around. this is a proof of concept at the moment.

So the problem was that i had this bit of XML that i needed to Deserialize
into an object but i didnt know what object to use untill runtime.


EXAMPLE:

<TR CST="2008-08-28T12:15:34+01:00" SRT="2008-08-28T12:15:40.12232+01:00" />


[Serializable]
[XmlType("TR")]
public class TimeSyncResponseMessage
{
[XmlAttribute("CST")]
public DateTime ClientSendTime;

[XmlAttribute("SRT")]
public DateTime ServerResponseTime;
}

[Serializable]
[XmlType("TS")]
public class TimeSyncRequestMessage
{
[XmlAttribute("T")]
public DateTime SendTime;
}


SOLUTION:

I always Wrap the XML in a <MT> tag, and i make all the other classes
Nested in a MessageTypes class. this ment i did not have to alter the
Objects in any way and the only change to the XML was the addition of a root
tag that can be stripped away.

Now i can Deserialize any one of the message types that i need to. using the
object i know MessageTypes.
If any of the Child Classes ever change all i need to do is replace it in
the MessageTypes class and that is the only change i will need to make.
Everything else will just work.


<MT><TR CST="2008-08-28T12:15:34+01:00"
SRT="2008-08-28T12:15:40.12232+01:00" /></MT>


using System.Xml.Serialization;
using System;
using System.Collections.Generic;
using System.Text;

namespace CommsViewer
{

[Serializable]
[XmlType("MT")]
public class MessageTypes
{

[Serializable]
[XmlType("TR")]
public class TimeSyncResponseMessage
{
[XmlAttribute("CST")]
public DateTime ClientSendTime;

[XmlAttribute("SRT")]
public DateTime ServerResponseTime;
}

[Serializable]
[XmlType("TS")]
public class TimeSyncRequestMessage
{
[XmlAttribute("T")]
public DateTime SendTime;
}

}

}

//===========================================
//FORM CODE

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Data.SqlClient;
using System.IO;
using System.Xml.Serialization;
using System.Xml;
using System.Reflection;


namespace CommsViewer
{


/// <summary>
/// This will try and Serialize an XML string into an object
/// </summary>
/// <param name="xml">Xml Text Reader to be Serialized</param>
/// <param name="objectType">The Object Type you want
returned</param>
/// <returns>And Object of the type requested</returns>
public static object XmlToObject(XmlTextReader xml, Type objectType)
{
object obj = null;

XmlSerializer xs = new XmlSerializer(objectType);
obj = xs.Deserialize(xml);

return obj;
}

//listBox1 Holds all the XML Strings i want to test
//listBox2 Is used to display the Objects fields and there values.
private void listBox1_SelectedIndexChanged(object sender, EventArgs
e)
{
listBox2.Items.Clear();

using (MemoryStream ms = new MemoryStream())
{
MemoryStream ms2 = new MemoryStream();

//Get the XML
byte[] data =
System.Text.Encoding.Default.GetBytes(listBox1.SelectedItem.ToString());

//Put In Stream
ms.Write(data, 0, data.Length);
ms.Seek(0, SeekOrigin.Begin);

//Duplicate Streem
ms2.Write(ms.GetBuffer(), 0, ms.GetBuffer().Length);
ms2.Seek(0, SeekOrigin.Begin);

//Create 2 XML Readers
XmlTextReader xrMT = new XmlTextReader(ms);
XmlTextReader xrInner = new XmlTextReader(ms2);

//Read the first
xrMT.Read();
xrMT.MoveToContent();
String xrMTTag = xrMT.LocalName;

//Create Object
MessageTypes commsData = (MessageTypes)XmlToObject(xrMT,
typeof(MessageTypes));


//Get Object Type
Type objectType = commsData.GetType();

//Get The Inner XML from second stream
xrInner.Read();
string innerXML = xrInner.ReadInnerXml();

//Create a XML reader from the Inner XML
data = System.Text.Encoding.Default.GetBytes(innerXML);
ms2 = new MemoryStream();
ms2.Write(data, 0, data.Length);
ms2.Seek(0, SeekOrigin.Begin);
xrInner = new XmlTextReader(ms2);

//Get the Tag name
xrInner.Read();
String xrTag = xrInner.LocalName;

//ms2.Close();

// get all the Nested Types for the object
foreach (Type mt in objectType.GetNestedTypes())
{
//Check all the Attributes
foreach (object oAtt in mt.GetCustomAttributes(true))
{
//If it si XML Type Attribute then Check if it is
the one we need
if (typeof(XmlTypeAttribute) == oAtt.GetType())
{
XmlTypeAttribute xa = (XmlTypeAttribute)oAtt;

//It the Tag is the same as the inner XML we
know we have the right Type
if (xrTag == xa.TypeName)
{
//Display what we are doing
listBox2.Items.Add(xa.TypeName);
listBox2.Items.Add(mt.Name);

Type myTypeA =
Type.GetType(string.Format("CommsViewer.{0}+{1}", objectType.Name,
mt.Name));

//object instance =
Activator.CreateInstance(myTypeA);

//Activator.CreateInstance(myTypeA)
innerMessage = (Activator.CreateInstance(myTypeA))XmlToObject(xrInner,
myTypeA);
object innerMessage = XmlToObject(xrInner,
myTypeA);

foreach (FieldInfo fl in
myTypeA.GetFields( ))
{
listBox2.Items.Add(string.Format("{0} =
{1}", fl.Name, fl.GetValue(innerMessage)));
}
}
}
}
}
}
}



}


//END of CODE


I hope somebody finds this usefull.

ink

















ink said:
Hi all,

I would like to be able to select the object used to Deserialize an XML
string based on the XML itself.
I need to do this because the XML is being read from a database and there
are a number of different objects. The XML is being inserted into the
database as a log of all trafice between systems and I have been asked to
write an app that will display the trafic in a human readable form. Want
to use reflection to get the actual object names. For Example.

Given the following XML:

<TR CST="2008-08-28T12:15:34+01:00" SRT="2008-08-28T12:15:40.12232+01:00"
/>

How can I figger out which object I need to use to Deserialize. This must
be at run time.

[Serializable]
[XmlType("TR")]
public class TimeSyncResponseMessage
{
[XmlAttribute("CST")]
public DateTime ClientSendTime;

[XmlAttribute("SRT")]
public DateTime ServerResponseTime;
}

[Serializable]
[XmlType("TS")]
public class TimeSyncRequestMessage
{
[XmlAttribute("T")]
public DateTime SendTime;
}


Some of the Rules would be:

1. The object them selves can not change as thes are being used in a live
system.
2. I can put them into another class like a parent if needed.

I was thinking that maybe I could look at the XML tag “TR†and then using
reflection loop through the members comparing the [XmlType("TR")] till I
found the correct object. The root tags are all unique.

But I don’t know how to do this? or if it is even possible.

Any sugestions?

Thanks.
Ink
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top