IXmlSerializable - c#

Could you guys help me I have a problem with deserialization via IXmlSerializable
var ArrayOfAccounts = new Accounts(); //This class structure I'm trying to read
Class Accounts:List<Session>{ }
Class Shedule{
public DateTime StartAt { get; set; }
public DateTime EndAt { get; set; }
}
Class Session:IXmlSerializable {
public string Name{get;set;}
public string Pass{get;set;}
public List<Shedule> Shedules = new List<Shedule>();
public void ReadXml(System.Xml.XmlReader reader){
//AND HERE IS A PROBLEM. I don't know how to implement right code here. I've tried
//code below, but this one works for the first account only, and doesn't restore others
Schedules.Clear();
XmlReader subR = reader.ReadSubtree();
if (reader.MoveToAttribute("Name"))
Name = reader.Value;
if (reader.MoveToAttribute("Password"))
Password = reader.Value;
reader.MoveToContent();
while (subR.ReadToFollowing("Schedule"))
{
XmlSerializer x = new XmlSerializer(typeof(Schedule));
object o = x.Deserialize(subR);
if (o is Schedule) Schedules.Add((Schedule)o);
}
}
And the xml itself looks like:
<Accounts>
<Session UserName="18SRO" Password="shalom99">
<Schedule>
<StartAt>0001-01-01T09:30:00</StartAt>
<EndAt>0001-01-01T16:00:00</EndAt>
</Schedule>
</Session>
</Accounts>

Since you've defined the classes, you should just be able to use XML Serialization attributes, and use the default XML deserializer.
Your structure doesn't look overly complicated, is there any particular reason you're not using serialization attributes instead of manually deserializing?

Re inherited fields... if you switch to DataContractSerializer, then fields are "opt in" rather than "opt out" - but you lose the ability to specify attributes (everything is an element). A trivial example:
[DataContract(Name="foo")]
public class Foo
{
[DataMember(Name="bar")]
public string Bar { get; set; }
public int ThisIsntSerialized {get;set;}
}
However - adding unexpected subclasses is a pain for both XmlSerializer and DataContractSerializer. Both can do it, but it isn't pretty...

Related

Control the order of attributes when serialising

Let me start off by saying I've seen the following questions:
Controlling order of serialization in C#: It is about element order
How to specify the order of XmlAttributes, using XmlSerializer: The answer just says "You don't"
net xmlserializer : keeping attributes order: Again says "you don't" or the alternate answer states that you can but you have to do all of the serialisation yourself
I've also seen people state that the order that you specify the attributes in the class is the order that they will be serialised. However, in my case I have a base class that has attributes too so this doesn't seem to work out.
I have the following two classes:
public class MyClass : BaseClass
{
[XmlAttribute()]
public string Value1 { get; set; }
public MyClass()
: base()
{
Value1 = string.Empty;
}
}
public class BaseClass
{
[XmlAttribute()]
public string Value2 { get; set; }
[XmlAttribute()]
public string Value3 { get; set; }
public BaseClass()
{
Value2 = string.Empty;
Value3 = string.Empty;
}
}
When serialised to an XML element's attributes they appear in the order, Value2, Value3, Value1. I'd like them to appear in the order Value1, Value2, Value3.
I use the following method to save the XML to disk:
public static void Save<T>(string path, T contents)
{
using (StreamWriter sw = new StreamWriter(File.OpenWrite(path)))
{
XmlSerializer xml = new XmlSerializer(typeof(T));
xml.Serialize(sw, contents, ns);
}
}
Is there any way I can specify the attribute order? Like with elements:
[XmlElementAttribute(Order = 1)]
To those who will say something like "Order doesn't matter" or "If it works why do you need to change the order" or "XML isn't meant to be readable". My answer is yes the order doesn't matter and it works. However, when I'm debugging my code and checking the XML to make sure it contains the values I expect, it is a lot easier to read if the attributes are in the order I want them to be in.

Xml serialization: serialize list of custom classes, derived from the same base class, using base class name for elements

EDIT
I made a mistake in my assumptions. I've answer myself to explain what was wrong and what I really need.
I have a problem to serialize a list of different custom classes. Each of them inherit from the same base class, and the Xml file should show elements with base class name.
This is how I want my Xml file looks like:
<TestDataMap>
<DataMap>
<Item Key="BoolStatus">True</Item>
<Item Key="IntStatus">77</Item>
</DataMap>
</TestDataMap>
My actual code is composed by a base class:
public class BaseItem
{
[XmlAttribute("Key")]
public string Key { get; set; }
[XmlText]
public string Value { get; set; }
}
and multiple custom classes, for example:
[XmlType("ItemBool")]
public class ItemBool : BaseItem
{
public ItemBool()
{
base.Key = string.Empty;
ValueBool = false;
}
[XmlIgnore]
public bool ValueBool
{
get { return bool.Parse(base.Value); }
set { base.Value = value.ToString(); }
}
}
[XmlType("ItemInt")]
public class ItemInt : BaseItem
{
public ItemInt()
{
base.Key = string.Empty;
ValueInt = int.MinValue;
}
[XmlIgnore]
public int ValueInt
{
get { return int.Parse(base.Value); }
set { base.Value = value.ToString(); }
}
}
I need to serialize all of these custom classes as a list of my base class, so I've done the following:
[XmlRoot("TestDataMap")]
public class TestDataMap
{
public TestDataMap()
{
DataMap = new List<BaseItem>();
}
// If I use the following I've got serialization exception...
//[XmlArrayItem("Item", typeof(ItemBool))]
//[XmlArrayItem("Item", typeof(ItemInt))]
[XmlArray("DataMap")]
[XmlArrayItem("Item", typeof(BaseItem))]
public List<BaseItem> DataMap { get; set; }
}
To test the serialization I'm using a button event:
private void button1_Click(object sender, EventArgs e)
{
TestDataMap testDataMap = new TestDataMap();
ItemBool itemBool = new ItemBool() { Key = "BoolStatus", ValueBool = true };
ItemInt itemInt = new ItemInt() { Key = "IntStatus", ValueInt = 77 };
testDataMap.DataMap.Add(itemBool);
testDataMap.DataMap.Add(itemInt);
TextWriter writer = new StreamWriter(#"c:\TempTest.xml");
Type[] dataMapTypes = new Type[] { typeof(ItemBool), typeof(ItemInt) };
XmlSerializer serializer = new XmlSerializer(typeof(TestDataMap), dataMapTypes);
serializer.Serialize(writer, testDataMap);
writer.Close();
}
But I obtain an output like this:
<TestDataMap>
<DataMap>
<Item Key="BoolStatus" xsi:type="ItemBool">True</Item>
<Item Key="IntStatus" xsi:type="ItemInt">77</Item>
</DataMap>
</TestDataMap>
that contains xsi:type information that I don't want to show...
What's wrong with my code? Any suggestions about what I have to change to obtain exactly the Xml structure I posted at the begin of the question?
When I asked this question I was too focus on the aesthetical result of the output xml file, instead of caring about the important point: xml serialization of custom classes and subsequent deserialization.
Even if I will be able to obtain exactly the xml elements as I originally posted, then I will have problems during deserialization: how can the deserializer knows, from a list of generic <Item> elements, which one is <ItemBool> and which one is <ItemInt>, without any other detail?
Thinking about this, I understand that what I've asked is not what I need, nor is the correct way to do xml serialization.
So I've changed the TestDataMap class I posted above to the following:
[XmlRoot("TestDataMap")]
public class TestDataMap
{
public TestDataMap()
{
DataMap = new List<BaseItem>();
}
[XmlArray("DataMap")]
[XmlArrayItem("ItemBool", typeof("ItemBool"))]
[XmlArrayItem("ItemInt", typeof("ItemInt"))]
public List<BaseItem> DataMap { get; set; }
}
To obtain this xml output:
<TestDataMap>
<DataMap>
<ItemBool Key="BoolStatus">True</ItemBool>
<ItemInt Key="IntStatus">77</ItemInt>
</DataMap>
</TestDataMap>
In this way, it's clear for the deserializer how it has to deserialize each type of <Item> element.

Serialize DateTime property to XML in C#

I´m a programming student and I would like to know if it is possible to change the format of a date when I serialize it in a xml file. This date is an attribute of an ObservableCollection of objects "Loan", this objects have two DateTime properties, one of the dates is a nullable object. I serialize all the collection including the date.
I would like to obtain in the xml file:
<OutDate> 15-03-2014 </OutDate>
<!--If the date is null I don´t want to appear the node-->
And I´m getting this:
<OutDate>2014-03-15T00:00:00</OutDate>
<InDate xsi:nil="true" />
This is part of my code project:
Part of my class Loan, already mark as serializable, looks like this:
private string isbn;
private string dni;
private DateTime dateOut;
private DateTime? dateIn;
// Setters and Gettters and constructors
This is the method for serialize:
// I will pass three collections to this method loans, books and clients
public void SerializeToXML<T>(string file, string node, ObservableCollection<T> collection)
{
XmlRootAttribute root = new XmlRootAttribute(node);
XmlSerializer serializer = new XmlSerializer(typeof(ObservableCollection<T>), root);
using (FileStream fs = new FileStream(file, FileMode.Create))
{
serializer.Serialize(fs, collection);
}
}
The call:
SerializeToXML<Loan>(_file, "Library", manager.LoansCollection);
Thnks.
If you don't want to implement IXmlSerializable, some DateTime to string conversion of a backing field should do the trick, something like this:
public class Loan
{
[XmlIgnore]
private DateTime _dateOut;
public string OutDate
{
get { return _dateOut.ToString("dd-MM-yyyy"); }
set { _dateOut = DateTime.Parse(value); }
}
}
Probably the simplest way to achieve this is to implement the IXmlSerializable interface on your class instead. Something along the following lines
public class Loan : IXmlSerializable
{
public void WriteXml(XmlWriter writer)
{
if(dateIn.HasValue)
{
writer.WriteElementString("dateIn", dateIn.Value.ToString());
}
}
}
On the read you'll need to read the Element name, if it's dateIn set that, otherwise set the appropriate value. Basically checking to see if it exists in the XML.
Have a look at the XmlElement attribute class (in System.Xml.Serialization). If that doesn't work then this answer shows how to use a proxy property
[XmlElement("TheDate", DataType = "date")]
public DateTime TheDate { get; set; }
I know it's late to get my answer marked as "the one", but you can have control over serialization without implementing complex interfaces or wrapping stuff as a workaround.
public DateTime? InDate { get; set }
public bool ShouldSerializeInDate()
{
return InDate.HasValue;
}
The C# XML Serializer has a not so well documented functionality. Every public property can have a method for turning on or off the serialization of the property. The method has to be called: ShouldSerializeXYZ where XYZ is the exact name of the property that you want to control.
See:
Xml serialization - Hide null values

What are XML element and attribute equivalents in C#?

I am trying to define C# objects based on this XML:
<UPDs LUPD="86">
<UPD ID="106">
<ER R="CREn">
<NU UID="1928456" />
<NU UID="1886294" />
<M>
<uN>bob · </uN>
<mO>fine :D</mO>
</M>
So far I have:
public class UDPCollection
{
List<UDP> UDPs;
public UDPCollection()
{
UDPs = new List<UDP>();
}
}
public class UDP
{
public int Id;
public List<ER> ERs;
public UDP(int id, List<ER> ers)
{
Id = id;
ERs = ers;
}
}
public class ER
{
public string LanguageR;
public ER(string languager)
{
LanguageR = languager;
}
}
My questions: What do elements map to in C#? Classes? What do attributes map to? Properties? Am I going about this the correct way?
Use the XmlSerializer class and the XmlRoot, XmlElement and XmlAttribute attributes. For example:
using System.Xml.Serialization;
...
[XmlRoot("UPDs")]
public class UDPCollection
{
// XmlSerializer cannot serialize List. Changed to array.
[XmlElement("UPD")]
public UDP[] UDPs { get; set; }
[XmlAttribute("LUPD")]
public int LUPD { get; set; }
public UDPCollection()
{
// Do nothing
}
}
[XmlRoot("UPD")]
public class UDP
{
[XmlAttribute("ID")]
public int Id { get; set; }
[XmlElement("ER")]
public ER[] ERs { get; set; }
// Need a parameterless or default constructor.
// for serialization. Other constructors are
// unaffected.
public UDP()
{
}
// Rest of class
}
[XmlRoot("ER")]
public class ER
{
[XmlAttribute("R")]
public string LanguageR { get; set; }
// Need a parameterless or default constructor.
// for serialization. Other constructors are
// unaffected.
public ER()
{
}
// Rest of class
}
The code to write out the XML is:
using System.Xml.Serialization;
...
// Output the XML to this stream
Stream stream;
// Create a test object
UDPCollection udpCollection = new UDPCollection();
udpCollection.LUPD = 86;
udpCollection.UDPs = new []
{
new UDP() { Id= 106, ERs = new [] { new ER() { LanguageR = "CREn" }}}
};
// Serialize the object
XmlSerializer xmlSerializer = new XmlSerializer(typeof(UDPCollection));
xmlSerializer.Serialize(stream, udpCollection);
Not that the XmlSerializer adds additional namepsaces but it can parse XML without them if needed. The output of the above is:
<?xml version="1.0"?>
<UPDs xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" LUPD="86">
<UPD ID="106">
<ER R="CREn" />
</UPD>
</UPDs>
Use the Deserialize() method to parse it from XML into an object.
XML elements and attributes don't necessarily map to anything in particular in C#. You can make them map to classes and properties if you want, but it's not required.
That said, if you want to map your existing XML to some sort of C# data structures, the way you're doing it seems reasonable - I'd just recommend replacing your public fields with actual properties, and maybe making the list properties a less specific type - say, IEnumerable, or ICollection, or IList if they really need to be in order.

Public fields/properties of a class derived from BindingList<T> wont serialize

I'm trying to serialize a class that derives from BindingList(Floor), where Floor is a simple class that only contains a property Floor.Height
Here's a simplified version of my class
[Serializable]
[XmlRoot(ElementName = "CustomBindingList")]
public class CustomBindingList:BindingList<Floor>
{
[XmlAttribute("publicField")]
public string publicField;
private string privateField;
[XmlAttribute("PublicProperty")]
public string PublicProperty
{
get { return privateField; }
set { privateField = value; }
}
}
I'll serialize an instance of CustomBindingList using the following code.
XmlSerializer ser = new XmlSerializer(typeof(CustomBindingList));
StringWriter sw = new StringWriter();
CustomBindingList cLIst = new CustomBindingList();
Floor fl;
fl = new Floor();
fl.Height = 10;
cLIst.Add(fl);
fl = new Floor();
fl.Height = 10;
cLIst.Add(fl);
fl = new Floor();
fl.Height = 10;
cLIst.Add(fl);
ser.Serialize(sw, cLIst);
string testString = sw.ToString();
Yet testString above ends getting set to the following XML:
<CustomBindingList xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">
<Floor Height="10" />
<Floor Height="10" />
<Floor Height="10" />
</CustomBindingList>"
How do I get "publicField" or "publicProperty to serialize as well?
The short answer here is that .NET generally expects something to be a collection xor to have properties. This manifests in a couple of places:
xml serialization; properties of collections aren't serialized
data-binding; you can't data-bind to properties on collections, as it implicitly takes you to the first item instead
In the case of xml serialization, it makes sense if you consider that it might just be a SomeType[] at the client... where would the extra data go?
The common solution is to encapsulate a collection - i.e. rather than
public class MyType : List<MyItemType> // or BindingList<...>
{
public string Name {get;set;}
}
public class MyType
{
public string Name {get;set;}
public List<MyItemType> Items {get;set;} // or BindingList<...>
}
Normally I wouldn't have a set on a collection property, but XmlSerializer demands it...
XML serialization handles collections in a specific way, and never serializes the fields or properties of the collection, only the items.
You could either :
implement IXmlSerializable to generate and parse the XML yourself (but it's a lot of work)
wrap your BindingList in another class, in which you declare your custom fields (as suggested by speps)
This is known issue with XML serialization and inheriting from collections.
You can read more info on this here : http://social.msdn.microsoft.com/Forums/en-US/asmxandxml/thread/0d94c4f8-767a-4d0f-8c95-f4797cd0ab8e
You could try something like this :
[Serializable]
[XmlRoot]
public class CustomBindingList
{
[XmlAttribute]
public string publicField;
private string privateField;
[XmlAttribute]
public string PublicProperty
{
get { return privateField; }
set { privateField = value; }
}
[XmlElement]
public BindingList<Floor> Floors = new BindingList<Floor>();
}
This means you can add floors by using Floors.Add and you will get the result you want, I hope, however, I didn't try it. Keep in mind that playing around with attributes is the key to XML serialization.

Categories

Resources