--> XmlSerializer and CollectionBase derived classes - Impersonation Failure

XmlSerializer and CollectionBase derived classes

This is something I've bumped my head with before and it happened again so this time I'll blog it...

Scenario: You've got a class derived from CollectionBase that you'd like to serialize using the XmlSerializer.
Example: (MyCollection that contains MyClasses, excuse my highly sophisticated code formatting but w.blogger doesn't play nicely with code)

[Serializable]

public class MyCollection : CollectionBase

{}

Output Required something like :

<?xml version="1.0" encoding="utf-8" ?>

<MyClasses>

       <MyClass/>

       <MyClass/>   

</MyClasses>

First problem, there's a couple of requirements from the XmlSerializer for it to serialize correctly. This is not problems as most people already create their Collections this way and it makes sense if you look at the serialization process followed by XmlSerializer.

MSDN : The XmlSerializer gives special treatment to classes that implement IEnumerable or ICollection. A class that implements IEnumerable must implement a public Add method that takes a single parameter. The Add method's parameter must be of the same type as is returned from the Current property on the value returned from GetEnumerator, or one of that type's bases. A class that implements ICollection (such as CollectionBase) in addition to IEnumerable must have a public Item indexed property (indexer in C#) that takes an integer, and it must have a public Count property of type integer. The parameter to the Add method must be the same type as is returned from the Item property, or one of that type's bases. For classes implementing ICollection, values to be serialized will be retrieved from the indexed Item property, not by calling GetEnumerator.

The second problem is that you can't add any XmlAttributes to your Collectionbase derived class to control the structure of the root element, the XmlSerializer will break and you'll get a System.InvalidOperationException : System.InvalidOperationException : XML attributes may not be specified for the type YourCollection.

The way it serializes by default is not exactly what I'm after and what you end up with is something like the following :


Output without any Xml Attributes :

  <?xml version="1.0" encoding="utf-8" ?>

<ArrayOfMyClasses xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

<MyClass/>

.

.

</ArrayOfMyClasses>

Take two, I remember something about XmlAttributeOverrides that can be used to override the defaults for the serialization process. Add the overrides for my collection but this is subject to the same constraints and you get the same exception. This happens somewhere when trying to create the temporary assemblies used to do the serialization/deserialization. Anyone know where or what the restrictions is on this?

Then I found this method to regain control over the element name. Just by adding a new XmlRootAttribute to the XmlSerializer you get back control of the process. Thanks thanks! Ionic Shade,whoever you are!

Basically to control how your Collectionbase class is serialized I've added the following two helper methods based on his code. One that changes the root element name to something specified by the user and the second will just derive a root name from the Collection Class. So by using this the Collection Base is serializing/deserializing as I hoped.

public static XmlSerializer CreateXmlCollectionBaseSerializer(Type type, string root)

              {     

                     XmlRootAttribute ra = new XmlRootAttribute();

                     ra.Namespace = string.Empty;

                     ra.ElementName = root;

                     XmlSerializer ser = new XmlSerializer(type,ra);

                     XmlSerializerNamespaces ns = new XmlSerializerNamespaces();

                     ns.Add("","");                   

                     return ser;

              }

 

public static XmlSerializer CreateXmlCollectionBaseSerializer(Type type)

              {

                     return CreateXmlCollectionBaseSerializer(type,type.Name);

              }

To also clear out the default namespaces have a look at his sample for more info.

 

Filed under:

Comments

# Corne said:

Unbelievable... that is exactly what I've been sitting with today, right down to the error you got (XML attributes may not be specified for the type....)
Thanks Armand!!

Tuesday, September 21, 2004 5:21 PM
# pieter@psh.co.za (Pieter Jansen van Vuuren) said:

Code format for web - http://www.manoli.net/csharpformat/

Tuesday, September 21, 2004 7:56 PM
# Armand du Plessis said:

Sorry for the formatting, was in a hurry. Will use the formatter next time.

Wednesday, September 22, 2004 8:21 PM
# bcblog said:

Very good, thanks,

And there maybe is a small bugs in the code

XmlSerializerNamespaces ns = new XmlSerializerNamespaces();

ns.Add("","");

never get used in the CreateXmlCollectionBaseSerializer().

To get ride of namespace attribute, put the 2 lines just before you call serializer.Serialize(mystream, myobject, ns);




Friday, September 24, 2004 2:03 AM
# Armand du Plessis said:

oops, i see thanks :-) problem snuck in when changing the code from test code into my solution. in the actual solution i can specify the serializer but don't have control over where the actual serialization happens. so for the moment i'm stuck with attributes. thanks for pointing out the redundant code.

Friday, September 24, 2004 9:42 AM
# Jon B said:

A simple workaround for this problem is creating a wrapper object for the collection. For example, I have a simple Product object with a matching ProductCollection object that derives from CollectionBase. Instead of serializing the ProductCollection, I created a ProductResults object that contains the ProductCollection object as a property as well as some paging properties. This way I can use the XmlAttribute metadata on the collection and control the xml output of the XmlSerializer.

Tuesday, September 28, 2004 12:35 AM
# Dan O said:

Jon B:

I have a wrapper object that exposes my CollectionBase derived object through a property. Unfortunately, the node corresponding to the collection is named using the ArrayOf syntax.

How did you overcome this problem in your situation???

I would appreciate your feedback.

Wednesday, September 29, 2004 12:01 AM
# Rein Petersen said:

I like the solution - until you want to deserialize the xml back into an object - doh.

I think I just have to accept that my collection can't be the root node and use the workaround Jon B described - too bad cuz otherwise, it serialized perfectly.

Friday, January 14, 2005 10:42 PM
# Ionic Shade said:

Glad it worked out for you . . .

Tuesday, January 18, 2005 5:13 AM
# Chirs Weber said:

Here is the solution to the issue raised by Rein Petersen. Just make sure you use the same root element that you told it to serialize as.

static public object Load(string XMLString, Type t)
{

XmlSerializer mySerializer =
new XmlSerializer(t,new System.Xml.Serialization.XmlRootAttribute("Packages"));

StringReader myReader =
new StringReader(XMLString);

return mySerializer.Deserialize(myReader);

} // Load

Friday, February 18, 2005 5:25 AM
# Chris Weber said:

Sorry let me make that a little clearer if I may:

static public object Load(string XMLString, Type t, string RootElementName)
{

XmlSerializer mySerializer =
new XmlSerializer(t,new System.Xml.Serialization.XmlRootAttribute(RootElementName));

StringReader myReader =
new StringReader(XMLString);

return mySerializer.Deserialize(myReader);

} // Load

Friday, February 18, 2005 5:27 AM
# Armand said:

Thanks Chris

Friday, February 18, 2005 5:52 AM
# Emilio said:

A problem arises when you have a collection class that is derived from CollectionBase and then you have such collection as a property of a parent class. Then the problem is when you try to use the XmlSerializer on the parent class.

public MyClass {
public MyCollection Items;
:
}

public MyCollection : CollectionBase
{
:
}

Monday, April 04, 2005 7:50 PM
# Emiliano said:

Is there a way to control the serialization process when a web service is performing it?

Thanks
Emiliano

Thursday, June 30, 2005 8:44 PM
# Rein Petersen said:

Hey Chris,

Thanks for the code... I have my object (derived from CollectionBase) serializing/deserializing as the root node very nicely. Except, I'm missing some attributes (public properties) that I had added to the collection. I'm thinking the XmlAttributeOverrides class might be the solution but thus far I haven't got it working...

Has anyone achieved serializing/deserializing an CollectionBase (derived) as the root node with attributes?

Rein

Wednesday, July 27, 2005 5:38 PM
# shailensukul said:

I have achieved seiralization using a custom attribute as the root node. Unfortunately, so far I have not been able to achieve deserialization from the generated XML because of the different name generated in the root node.

To see the serialization solution, please see: http://shailensukul.blogspot.com

Thursday, November 17, 2005 11:35 PM
# Serialization Question | keyongtech said:

Pingback from  Serialization Question | keyongtech

Thursday, January 22, 2009 6:25 AM