I have the following XML:
<Root>
<EventSet>
<Event>
<id>12345</id>
<rant>
<localTime>
<dst>true</dst>
<time>7/2/2012 14:30</time>
</localtime>
<randomRant>
<random>to illustrate point</random>
<rant>help me!</rant>
</randomRant>
</rant>
</Event>
<Event>/*another event*/</Event>
<Event>/*another event*/</Event> //etc
</EventSet>
</Root>
I want to map this to:
[Serializable]
public class Events
{
public List<Event> events { get; set; }
}
[Serializable]
public class Event
{
public int id { get; set; }
public Rant rant { get; set; } //this is where I get confused
}
QUESTION: How do I serialize the tags within <rant>? Do I continue to make serialized classes of the parent until the child tags have no children? For example, below:
[Serializable]
public class Rant
{
public LocalTime localTime { get; set; }
public RandomRant randomRant { get; set; }
}
[Serializable]
public class LocalTime
{
public bool dst { get; set; }
public DateTime time { get; set; }
}
[Serializable]
public class RandomRant
{
public string random { get; set; }
public string rant { get; set; }
}
Or is there a better approach?
EDIT: A given event has one and only one id, and one and only one rant. For the sake of my question, assume my types are valid. I am looking to deserialize nested XML into an object.
The example you gave seems fine to me.
As for the time values, the date format in your example is not compatible with xsd:dateTime, so you can't just do
public DateTime time { get; set; }
This will not be serialized. Although you can hack this using a custom XmlSerializer, a more simple approach is to use:
[XmlIgnore]
public DateTime _time {
get { return DateTime.ParseExact(this.time, "MM/dd/YYYY HH:mm", CultureInfo.InvariantCulture);} // or use some specific culture here.
}
[XmlElement]
public string time { get; set; }
UPDATE:
public List<Event> events will be de/serialized as:
<events>
<Event></Event>
<Event></Event>
<Event></Event>
</events>
You can make it as:
[XmlElement("Event")]
public List<Event> events {get; set}
and it will be serialized as
<Event></Event>
<Event></Event>
<Event></Event>
without wrapper.
Or, using:
[XmlArray("EventSet")]
[XmlArrayItem("Event")]
public List<Event> events {get; set}
will be serialized as described in the example
<EventSet>
<Event></Event>
<Event></Event>
</EventSet>
And of course the root element:
[XmlRoot("Root")]
public class RootElement{
[XmlArray("EventSet")]
[XmlArrayItem("Event")]
public List<Event> events {get; set}
}
Related
XML to be deserialized:
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<changes next="296">
<change>
<objectid>E702C43C-E04B-450B-BEBC-76646AB299C5</objectid>
<parentid>ED98C97F-A202-48ED-AEEA-34362508A30B</parentid>
<objecttype>file</objecttype>
<listentype>remove</listentype>
</change>
<change>
<objectid>3A242975-CEF0-432B-A997-B33D85C138C8</objectid>
<parentid>ED98C97F-A202-48ED-AEEA-34362508A30B</parentid>
<objecttype>file</objecttype>
<listentype>add</listentype>
</change>
</changes>
Data models used:
[XmlRoot("changes")]
public class ChangeListener
{
public List<Change> Changes { get; set; }
}
[XmlRoot("change")]
public class Change
{
[XmlElement("objectid")]
public Guid objectid { get; set; }
[XmlElement("parentid")]
public Guid parentid { get; set; }
[XmlElement("objecttype")]
public string objecttype { get; set; }
[XmlElement("listentype")]
public string listentype { get; set; }
}
Deserialization code, here result is above xml in string format:
(ChangeListener)new XmlSerializer(typeof(ChangeListener)).Deserialize(new MemoryStream(Encoding.UTF8.GetBytes(result)))
But I am getting errors for this code; I also tried couple of things e.g. marked Changes property of ChangeListener class with [XmlElement("changes")] instead of marking class as xmlroot but it also did not worked.
Kindly suggest good approach for this issues.
The problem is that the Changes List in the ChangeListener is confusing the serializer because there's nothing called 'Changes' in the XML.
The only change we need to make is to annotate the declaration of Changes with [XmlElement("change")] as below:
[XmlRoot("changes")]
public class ChangeListener
{
[XmlElement("change")]
public List<Change> Changes { get; set; }
}
The XML shown then deserializes correctly.
The Class for mentioned XML should look like below.
[XmlRoot(ElementName="change")]
public class Change {
[XmlElement(ElementName="objectid")]
public string Objectid { get; set; }
[XmlElement(ElementName="parentid")]
public string Parentid { get; set; }
[XmlElement(ElementName="objecttype")]
public string Objecttype { get; set; }
[XmlElement(ElementName="listentype")]
public string Listentype { get; set; }
}
[XmlRoot(ElementName="changes")]
public class Changes {
[XmlElement(ElementName="change")]
public List<Change> Change { get; set; }
[XmlAttribute(AttributeName="next")]
public string Next { get; set; }
}
Try changing the type of objectid and parentid from Guid to string.
Share the error details if you still get errors.
Consider the following class:
[Serializable]
public class MyClass
{
[XmlAttribute("id")]
public int ID { get; set; }
[XmlArrayItem("User")]
public List<string> Users { get; set; }
[XmlArrayItem("Role")]
public List<string> Roles { get; set; }
}
Now I want to transform the class definition to get following expected result of XML serialization for an instance of the class:
<MyClass id="123">
<Access>
<User>User A</User>
<User>User B</User>
<User>User C</User>
<Role>Role A</Role>
<Role>Role B</Role>
<Role>Role C</Role>
</Access>
</MyClass>
Moreover it must be deserializable. I tried to apply [Xml Array("Access")] attribute to both Users and Roles properties, but an InvalidOperationException occurs.
It would be great if the question can be solved without IXmlSerializable implementation.
It looks like you just need to make an "Access" class to nest your Users/Roles.
[XmlRoot(ElementName="Access")]
public class Access {
[XmlElement(ElementName="User")]
public List<string> User { get; set; }
[XmlElement(ElementName="Role")]
public List<string> Role { get; set; }
}
[XmlRoot(ElementName="MyClass")]
public class MyClass {
[XmlElement(ElementName="Access")]
public Access Access { get; set; }
[XmlAttribute(AttributeName="id")]
public string Id { get; set; }
}
I am working in C#, Visual Studio 2015, targeting .NET 4.5. We have existing systems (some written in Java, some legacy code in C++, etc.) that already exchange XML in particular formats. I have studied XMLSerializer, DataContractSerializer, and looked briefly at the ISerializable Interface. While I think I already know the answer (ISerializable), I figured I would see if anyone has any clever ideas or solutions for duplicating the XML format we need without coding it all ourselves. We have four different XML messages to duplicate, here is just one of them:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE message SYSTEM "work_request_20.dtd">
<message message_id="1005" message_dt="01/21/2008 09:50:23.221 AM" message_type="Work_Request" message_sub_type="New" message_dtd_version_number="2.0">
<header>
<from_application_id>3367e115-c873-4ac9-a1dd-7e45231dc3d5</from_application_id>
<to_application_id>35e0cca2-e423-4ffe-ba07-7d056775c228</to_application_id>
</header>
<body>
<work_request requisition_number="REQ44">
<client client_id="44">
<first_name>Barak</first_name>
<last_name>Obama</last_name>
</client>
<patient patient_id="4444" patient_species="CANINE" patient_gender="MALE_INTACT">
<patient_name>Bo</patient_name>
<patient_breed>Portuguese Water Dog</patient_breed>
<patient_birth_dt>04/04/2004</patient_birth_dt>
<patient_weight patient_weight_uom="lbs">
<weight>44.4</weight>
</patient_weight>
</patient>
<doctor>
<first_name>Surgeon</first_name>
<last_name>General</last_name>
</doctor>
<service_add>
<service_cd>ALB</service_cd>
<service_cd>GLU</service_cd>
<service_cd>BUN</service_cd>
</service_add>
</work_request>
</body>
</message>
If anyone can suggest any brilliant shortcuts as compared to the obvious solution, we would be forever grateful.
Thanks in advance.
Serializing with XmlSerializer is likely going to be the easiest solution. If classes auto-generated by xsd.exe are too bloated, you can use another code generation tool such as https://xmltocsharp.azurewebsites.net/ to generate them for you -- or even do it yourself manually.
For instance, I generated the following types using https://xmltocsharp.azurewebsites.net/ then made a few manual tweaks that are mentioned in comments:
[XmlRoot(ElementName="header")]
public class Header {
// I modified the types of these properties from string to Guid
[XmlElement(ElementName="from_application_id")]
public Guid From_application_id { get; set; }
[XmlElement(ElementName="to_application_id")]
public Guid To_application_id { get; set; }
}
[XmlRoot(ElementName="client")]
public class Client {
[XmlElement(ElementName="first_name")]
public string First_name { get; set; }
[XmlElement(ElementName="last_name")]
public string Last_name { get; set; }
[XmlAttribute(AttributeName="client_id")]
public string Client_id { get; set; }
}
[XmlRoot(ElementName="patient_weight")]
public class Patient_weight {
// I changed weight from string to decimal
[XmlElement(ElementName="weight")]
public decimal Weight { get; set; }
[XmlAttribute(AttributeName="patient_weight_uom")]
public string Patient_weight_uom { get; set; }
}
[XmlRoot(ElementName="patient")]
public class Patient {
[XmlElement(ElementName="patient_name")]
public string Patient_name { get; set; }
[XmlElement(ElementName="patient_breed")]
public string Patient_breed { get; set; }
[XmlElement(ElementName="patient_birth_dt")]
public string Patient_birth_dt { get; set; }
[XmlElement(ElementName="patient_weight")]
public Patient_weight Patient_weight { get; set; }
[XmlAttribute(AttributeName="patient_id")]
public string Patient_id { get; set; }
[XmlAttribute(AttributeName="patient_species")]
public string Patient_species { get; set; }
[XmlAttribute(AttributeName="patient_gender")]
public string Patient_gender { get; set; }
}
[XmlRoot(ElementName="doctor")]
public class Doctor {
[XmlElement(ElementName="first_name")]
public string First_name { get; set; }
[XmlElement(ElementName="last_name")]
public string Last_name { get; set; }
}
[XmlRoot(ElementName="work_request")]
public class Work_request {
[XmlElement(ElementName="client")]
public Client Client { get; set; }
[XmlElement(ElementName="patient")]
public Patient Patient { get; set; }
[XmlElement(ElementName="doctor")]
public Doctor Doctor { get; set; }
// I simplied this into a list of strings.
[XmlArray(ElementName="service_add")]
[XmlArrayItem("service_cd")]
public List<string> Service_add { get; set; }
[XmlAttribute(AttributeName="requisition_number")]
public string Requisition_number { get; set; }
}
[XmlRoot(ElementName="body")]
// I renamed this to WorkRequestBody
public class WorkRequestBody
{
[XmlElement(ElementName="work_request")]
public Work_request Work_request { get; set; }
}
[XmlRoot(ElementName="message")]
// I made this generic to account for multiple types of messge.
public class Message<T> where T : class, new()
{
[XmlElement(ElementName="header")]
public Header Header { get; set; }
[XmlElement(ElementName="body")]
public T Body { get; set; }
[XmlAttribute(AttributeName="message_id")]
public string Message_id { get; set; }
[XmlAttribute(AttributeName="message_dt")]
public string Message_dt { get; set; }
[XmlAttribute(AttributeName="message_type")]
public string Message_type { get; set; }
[XmlAttribute(AttributeName="message_sub_type")]
public string Message_sub_type { get; set; }
[XmlAttribute(AttributeName="message_dtd_version_number")]
public string Message_dtd_version_number { get; set; }
}
Using these types, I can now deserialize and re-serialize your XML into a Message<WorkRequestBody> and the resulting re-serialized XML is equivalent to the original XML, according to XNode.DeepEquals(). Sample fiddle.
To include a <!DOCTYPE ...> in the re-serialized XML, see this question.
Implementing IXmlSerializable for the root object is roughly the same difficulty as manually reading and writing your entire object graph with an XmlReader and XmlWriter. It's certainly possible but will require more work that using XmlSerializer. You'll still need to design POCOs to hold the data in memory, so it will be easier to use a serializer to read and write those POCOs automatically whenever possible. See here for a guide on how to do it correctly.
Reading and writing with LINQ to XML would represent an intermediate level of difficulty.
Finally, DataContractSerializer is not appropriate since there is no way to indicate that certain c# properties should be serialized as XML attributes (source).
How can I serialize the given object into JSON but only include properties with the [DataMember] attribute.
User MyUser = new User();
string MessageJson = new System.Web.Script.Serialization.JavaScriptSerializer().Serialize(MyUser);
public class User
{
[DataMember]
public string username { get; set; }
public string password { get; set; }
}
You need to use DataContractJsonSerializer for that.
Note that, I think you'll also need DataContract attribute on the class.
You can use JSON.Net.
If a class has many properties and you only want to serialize a small subset of them then adding JsonIgnore to all the others will be tedious and error prone. The way to tackle this scenario is to add the DataContractAttribute to the class and DataMemberAttributes to the properties to serialize. This is opt-in serialization, only the properties you mark up with be serialized, compared to opt-out serialization using JsonIgnoreAttribute.
[DataContract]
public class Computer
{
// included in JSON
[DataMember]
public string Name { get; set; }
[DataMember]
public decimal SalePrice { get; set; }
// ignored
public string Manufacture { get; set; }
public int StockCount { get; set; }
public decimal WholeSalePrice { get; set; }
public DateTime NextShipmentDate { get; set; }
}
You can place the [ScriptIgnore] attribute on the properties that you do not want to include in your result.
My application has 2 parts, an android client and a windows print server coded in c#.
I used Xstream in java to convert my object to XML (in android). here's a part of it:
<ROOT>
<id>1</id>
<serial>92000</serial>
<date>2013/2/15</date>
<ITEMS>
<ITEM>
<name>/**SOMETHING**/</name>
<idd>/**SOMETHING**/</idd>
<pd>/**SOMETHING**/</pd>
<ed>/**SOMETHING**/</ed>
</ITEM>
<ITEM>
<name>/**SOMETHING**/</name>
<idd>/**SOMETHING**/</idd>
<pd>/**SOMETHING**/</pd>
<ed>/**SOMETHING**/</ed>
</ITEM>
<ITEM>
<name>/**SOMETHING**/</name>
<idd>/**SOMETHING**/</idd>
<pd>/**SOMETHING**/</pd>
<ed>/**SOMETHING**/</ed>
</ITEM>
</ITEMS>
</ROOT>
as you can see, I have 2 object types, one ROOT object type, and a nested list of second object type named ITEMS.
I can read the ROOT object's name,serial and date, but for the nested list of ITEMS object, it always return null.
the class for the Root class in c# is:
[XmlRoot("ROOT")]
public class ROOT_CLASS
{
[XmlElement("id")]
public string idVar{ get; set; }
[XmlElement("serial")]
public string serialVar{ get; set; }
[XmlElement("date")]
public string dateVar{ get; set; }
[XmlArray("ITEMS")]
[XmlArrayItem("ITEM")]
public List<NESTED_CLASS> oi { get; set; }
}
and here is the nested object class:
[XmlRoot("ITEM")]
public class NESTED_CLASS
{
[XmlElement("name")]
public string nameVV; { get; set; }
[XmlElement("idd")]
public string iddVV; { get; set; }
[XmlElement("pd")]
public string pdVV; { get; set; }
[XmlElement("ed")]
public string edVV; { get; set; }
}
okay, How can I deserialize the NESTED_CLASS list out of this xml? as I said, I always get a null out of it. please help me.
thanks...
Use XmlArray attribute
[XmlRoot(ElementName = "ROOT")]
public class Root
{
public int id { get; set; }
public int serial { get; set; }
public string date { get; set; }
[XmlArray(ElementName = "ITEMS")]
[XmlArrayItem("ITEM")]
public List<RootItem> Items { get; set; }
}
public class RootItem
{
public string name { get; set; }
public string idd { get; set; }
public string pd { get; set; }
public string ed { get; set; }
}
Instead of doing this manually, you could also generate your C# class by using xsd.exe. This may be easier, and faster in case this xml changes over time; and you don't have to worry about problems you are having now.
See Generate C# class from XML for an example on how to do that.
Note that if you are working with very large xml files, this approach may not be the best one as it loads the entire xml into memory. If this is the case, you might want to use this approach instead.