XmlSerialize a custom collection with an Attribute - c#

I've got a simple class that inherits from Collection and adds a couple of properties. I need to serialize this class to XML, but the XMLSerializer ignores my additional properties.
I assume this is because of the special treatment that XMLSerializer gives ICollection and IEnumerable objects. What's the best way around this?
Here's some sample code:
using System.Collections.ObjectModel;
using System.IO;
using System.Xml.Serialization;
namespace SerialiseCollection
{
class Program
{
static void Main(string[] args)
{
var c = new MyCollection();
c.Add("Hello");
c.Add("Goodbye");
var serializer = new XmlSerializer(typeof(MyCollection));
using (var writer = new StreamWriter("test.xml"))
serializer.Serialize(writer, c);
}
}
[XmlRoot("MyCollection")]
public class MyCollection : Collection<string>
{
[XmlAttribute()]
public string MyAttribute { get; set; }
public MyCollection()
{
this.MyAttribute = "SerializeThis";
}
}
}
This outputs the following XML (note MyAttribute is missing in the MyCollection element):
<?xml version="1.0" encoding="utf-8"?>
<MyCollection xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<string>Hello</string>
<string>Goodbye</string>
</MyCollection>
What I want is
<MyCollection MyAttribute="SerializeThis"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<string>Hello</string>
<string>Goodbye</string>
</MyCollection>
Any ideas? The simpler the better. Thanks.

Collections generally don't make good places for extra properties. Both during serialization and in data-binding, they will be ignored if the item looks like a collection (IList, IEnumerable, etc - depending on the scenario).
If it was me, I would encapsulate the collection - i.e.
[Serializable]
public class MyCollectionWrapper {
[XmlAttribute]
public string SomeProp {get;set;} // custom props etc
[XmlAttribute]
public int SomeOtherProp {get;set;} // custom props etc
public Collection<string> Items {get;set;} // the items
}
The other option is to implement IXmlSerializable (quite a lot of work), but that still won't work for data-binding etc. Basically, this isn't the expected usage.

If you do encapsulate, as Marc Gravell suggests, the beginning of this post explains how to get your XML to look exactly like you describe.
http://blogs.msdn.com/youssefm/archive/2009/06/12/customizing-the-xml-for-collections-with-xmlserializer-and-datacontractserializer.aspx
That is, instead of this:
<MyCollection MyAttribute="SerializeThis"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Items>
<string>Hello</string>
<string>Goodbye</string>
<Items>
</MyCollection>
You can have this:
<MyCollection MyAttribute="SerializeThis"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<string>Hello</string>
<string>Goodbye</string>
</MyCollection>

As Neil Whitaker suggests and in case his link dies..
Create an inner collection to store the strings and apply the XmlElement attribute to mask the collection name. Produces the same xml output as if MyCollection inherited from Collection, but also serializes attributes on the parent element.
[XmlRoot("MyCollection")]
public class MyCollection
{
[XmlAttribute()]
public string MyAttribute { get; set; }
[XmlElement("string")]
public Collection<string> unserializedCollectionName { get; set; }
public MyCollection()
{
this.MyAttribute = "SerializeThis";
this.unserializedCollectionName = new Collection<string>();
this.unserializedCollectionName.Add("Hello");
this.unserializedCollectionName.Add("Goodbye");
}
}

I've been fighting with the same issue Romaroo is (wanting to add properties to the xml serialization of a class that implements ICollection). I have not found any way to expose properties that are in the collection class. I even tried using the XmlAttribute tag and making my properties show up as attributes of the root node, but no luck there either. I was however able to use the XmlRoot tag on my class to rename it from "ArrayOf...". Here are some references in case you're interested:
MilkCarton.com
Microsoft Forum Posting
Diranieh - look halfway down

Sometimes you just want to do what you just want to do; the Framework be damned.
I posted an answer here Property of a List<T> Not Deserialized
that does what the OP wanted to do.

Related

Add XmlAttribute to C# Class for serialization

I'm having a bit of a head scratching moment here, as I think I'm doing this correctly!
I need to create an xml file as below, (I've left out the namespace declarations)
<races>
<race racename="race one">
<horse>
<name>Silver</name>
<age>6</name>
</horse>
</race>
</races>
Class races is a collection of class race, and class race is a collection of class horse. Below is the relevant code for race class which I have and is causing the problem (I think at least).
[Serializable]
[XmlType("race")]
public class Race : CollectionBase
{
private string _raceName;
[XmlAttribute("racename")]
public string RaceName
{
get
{
return this._raceName;
}
set
{
this._raceName = value;
}
}
I have the xml file building as expected EXCEPT the attribute racename is not being serialized. It is definitely being assigned to the race object before serialization. Any thoughts? I'm obviously missing something somewhere but I'm not sure how I'd even test where it's failing. Any help would be greatly appreciated!
Eoin.
In classes that implement IEnumerable, only collections are serialized, not public properties.
Use the Horse collection inside the Race class and remove :CollectionBase
[Serializable]
[XmlType("race")]
public class Race
{
[XmlElement("horse")]
public List<Horse> Horses { get; set; }
[XmlAttribute("racename")]
public string RaceName
{
get;
set;
}
public Race()
{
Horses = new List<Horse>();
}
}
Result
<race xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" racename="race one">
<horse>
<Name>Silver</Name>
<Age>6</Age>
</horse>
</race>
I suggest you to create a test console application in the solution and test code like this http://pastebin.com/bP340WmR if it works fine create a collections of objects and try to serialize. Doing it step by step will help to understand where exactly the problem is.

C# Structure for the settings class

In the app I'm currently working with the user is allowed to store a lot of settings divided in different categories in XML files.
The XML could look like
<settings>
<setting2>
<setting3>
<value1>foo</value1>
<value2>bar</value2>
</setting3>
<value3>A list of strings</value3>
</setting2>
<setting4>
<value4>An integer</value4>
</setting4>
</settings>
The thing I'm confused about is how to best structure the class containing this information, I started out by doing a recursive class containing a list of categories and values, and now I remembered that there is a tree collection class in Java and something like that would be the best solution?
What do you guys recommend?
Why not just load the entire XML file into an XPathDocument? It's easy to use and quite speedy.
You could access individual settings like so:
XPathDocument xmldoc = new XPathDocument(settingsFile);
XPathNavigator nav = xmldoc.CreateNavigator();
foreach (XPathNavigator node in nav.Select("settings/setting2/setting3"))
{
...
}
I would stand for more OO approach.
public abstract class SettingAbstract
{
public abstract List<Setting> Children {get;set;}
public abstract List<object> Values {get;set}
public XmlNode SaveMe();
}
and after concrete implementation for every setting type
public Setting2 : SettingAbstract
{
//concrete implementation
}
public Setting3 : SettingAbstract
{
//concrete implementation
}
......
......
Something like this.

How to serialize class members as attributes to xml in C#

I am trying to serialize my object to xml. A serializer seemingly serializes all data members as children, but I want to serialize all members as attributes, not children.
Here's a code example:
[DataContract]
public class MyDataClass
{
[DataMember]
int foo = 24;
[DataMember]
string bar = "brabrabra";
}
This will be serialized as following xml when I use DataContractSerializer:
<MyDataClass xmlns="..." xmlns:i="...">
<foo>24</foo>
<bar>brabrabra</bar>
</MyDataClass>
However, I want to serialize it as following xml somehow:
<MyDataClass xmlns="..." xmlns:i="..." foo="24" bar="brabrabra" />
Is there any way to serialize like that? Or, should I write my own serializer to realize it?
For reference, I am using DataContract serializer in this sample, but I can change it to a normal XmlSerializer or another one if there's a better one.
Hope someone knows about this.
Aki
Have a look at XMLAttribute. Only works with XMLSerializer though.
You can use a simple XmlSerializer to achieve this, in the following way:
[Serializable]
public class SerializationTest2
{
[XmlAttributeAttribute]
public string MemberA { get; set; }
}
[Test]
public void TestSerialization()
{
var d2 = new SerializationTest2();
d2.MemberA = "test";
new XmlSerializer(typeof(SerializationTest2))
.Serialize(File.OpenWrite(#"c:\temp\ser2.xml"), d2);
}
Put [XmlAttribute] before foo and bar declaration.
Great ref: http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializer.aspx

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.

IXmlSerializable

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...

Categories

Resources