C# Custom Serialization - Using TypeConverter - c#

So I need to serialize a generic dictionary like Dictionary<long, List<MyClass>>. I need to serialize it to store it in the ViewState of an ASP.Net application.
I found an example of using TypeConverter to convert my class to a string to be serialized, but I get an error message saying MyClass is not marked as serializable.
Here is the code for my class..
[TypeConverter (typeof(MyClass_Converter))]
public class MyClass
{
// some properties
}
public class MyClass_Converter : System.ComponentModel.TypeConverter
{
public override bool CanConvertTo(...)
{
// code
}
// CanConvertFrom, ConvertFrom, ConvertTo methods
}
Then when I want to serialize it, I am using this code...
LosFormatter los = new LosFormatter();
StringWriter sw = new StringWriter();
los.Serialize(sw, hiddenData);
String resultSt = sw.GetStringBuilder().ToString();
ViewState["HiddenData"] = resultSt;
Am I doing something wrong?

Add the [Serializable] attribute to your class.
http://msdn.microsoft.com/en-us/library/system.serializableattribute.aspx

Related

Cannot serialize member T of type interface in C# [duplicate]

I would like to XML serialize an object that has (among other) a property of type IModelObject (which is an interface).
public class Example
{
public IModelObject Model { get; set; }
}
When I try to serialize an object of this class, I receive the following error:
"Cannot serialize member Example.Model of type Example because it is an interface."
I understand that the problem is that an interface cannot be serialized. However, the concrete Model object type is unknown until runtime.
Replacing the IModelObject interface with an abstract or concrete type and use inheritance with XMLInclude is possible, but seems like an ugly workaround.
Any suggestions?
This is simply an inherent limitation of declarative serialization where type information is not embedded within the output.
On trying to convert <Flibble Foo="10" /> back into
public class Flibble { public object Foo { get; set; } }
How does the serializer know whether it should be an int, a string, a double (or something else)...
To make this work you have several options but if you truly don't know till runtime the easiest way to do this is likely to be using the XmlAttributeOverrides.
Sadly this will only work with base classes, not interfaces. The best you can do there is to ignore the property which isn't sufficient for your needs.
If you really must stay with interfaces you have three real options:
Hide it and deal with it in another property
Ugly, unpleasant boiler plate and much repetition but most consumers of the class will not have to deal with the problem:
[XmlIgnore()]
public object Foo { get; set; }
[XmlElement("Foo")]
[EditorVisibile(EditorVisibility.Advanced)]
public string FooSerialized
{
get { /* code here to convert any type in Foo to string */ }
set { /* code to parse out serialized value and make Foo an instance of the proper type*/ }
}
This is likely to become a maintenance nightmare...
Implement IXmlSerializable
Similar to the first option in that you take full control of things but
Pros
You don't have nasty 'fake' properties hanging around.
you can interact directly with the xml structure adding flexibility/versioning
Cons
you may end up having to re-implement the wheel for all the other properties on the class
Issues of duplication of effort are similar to the first.
Modify your property to use a wrapping type
public sealed class XmlAnything<T> : IXmlSerializable
{
public XmlAnything() {}
public XmlAnything(T t) { this.Value = t;}
public T Value {get; set;}
public void WriteXml (XmlWriter writer)
{
if (Value == null)
{
writer.WriteAttributeString("type", "null");
return;
}
Type type = this.Value.GetType();
XmlSerializer serializer = new XmlSerializer(type);
writer.WriteAttributeString("type", type.AssemblyQualifiedName);
serializer.Serialize(writer, this.Value);
}
public void ReadXml(XmlReader reader)
{
if(!reader.HasAttributes)
throw new FormatException("expected a type attribute!");
string type = reader.GetAttribute("type");
reader.Read(); // consume the value
if (type == "null")
return;// leave T at default value
XmlSerializer serializer = new XmlSerializer(Type.GetType(type));
this.Value = (T)serializer.Deserialize(reader);
reader.ReadEndElement();
}
public XmlSchema GetSchema() { return(null); }
}
Using this would involve something like (in project P):
public namespace P
{
public interface IFoo {}
public class RealFoo : IFoo { public int X; }
public class OtherFoo : IFoo { public double X; }
public class Flibble
{
public XmlAnything<IFoo> Foo;
}
public static void Main(string[] args)
{
var x = new Flibble();
x.Foo = new XmlAnything<IFoo>(new RealFoo());
var s = new XmlSerializer(typeof(Flibble));
var sw = new StringWriter();
s.Serialize(sw, x);
Console.WriteLine(sw);
}
}
which gives you:
<?xml version="1.0" encoding="utf-16"?>
<MainClass
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Foo type="P.RealFoo, P, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
<RealFoo>
<X>0</X>
</RealFoo>
</Foo>
</MainClass>
This is obviously more cumbersome for users of the class though avoids much boiler plate.
A happy medium may be merging the XmlAnything idea into the 'backing' property of the first technique. In this way most of the grunt work is done for you but consumers of the class suffer no impact beyond confusion with introspection.
The solution to this is using reflection with the DataContractSerializer. You don't even have to mark your class with [DataContract] or [DataMember]. It will serialize any object, regardless of whether it has interface type properties (including dictionaries) into xml. Here is a simple extension method that will serialize any object into XML even if it has interfaces (note you could tweak this to run recursively as well).
public static XElement ToXML(this object o)
{
Type t = o.GetType();
Type[] extraTypes = t.GetProperties()
.Where(p => p.PropertyType.IsInterface)
.Select(p => p.GetValue(o, null).GetType())
.ToArray();
DataContractSerializer serializer = new DataContractSerializer(t, extraTypes);
StringWriter sw = new StringWriter();
XmlTextWriter xw = new XmlTextWriter(sw);
serializer.WriteObject(xw, o);
return XElement.Parse(sw.ToString());
}
what the LINQ expression does is it enumerates each property,
returns each property that is an interface,
gets the value of that property (the underlying object),
gets the type of that concrete object
puts it into an array, and adds that to the serializer's list of known types.
Now the serializer knows how about the types it is serializing so it can do its job.
If you know your interface implementors up-front there's a fairly simple hack you can use to get your interface type to serialize without writing any parsing code:
public interface IInterface {}
public class KnownImplementor01 : IInterface {}
public class KnownImplementor02 : IInterface {}
public class KnownImplementor03 : IInterface {}
public class ToSerialize {
[XmlIgnore]
public IInterface InterfaceProperty { get; set; }
[XmlArray("interface")]
[XmlArrayItem("ofTypeKnownImplementor01", typeof(KnownImplementor01))]
[XmlArrayItem("ofTypeKnownImplementor02", typeof(KnownImplementor02))]
[XmlArrayItem("ofTypeKnownImplementor03", typeof(KnownImplementor03))]
public object[] InterfacePropertySerialization {
get { return new[] { InterfaceProperty }; ; }
set { InterfaceProperty = (IInterface)value.Single(); }
}
}
The resulting xml should look something along the lines of
<interface><ofTypeKnownImplementor01><!-- etc... -->
You can use ExtendedXmlSerializer. This serializer support serialization of interface property without any tricks.
var serializer = new ConfigurationContainer().UseOptimizedNamespaces().Create();
var obj = new Example
{
Model = new Model { Name = "name" }
};
var xml = serializer.Serialize(obj);
Your xml will look like:
<?xml version="1.0" encoding="utf-8"?>
<Example xmlns:exs="https://extendedxmlserializer.github.io/v2" xmlns="clr-namespace:ExtendedXmlSerializer.Samples.Simple;assembly=ExtendedXmlSerializer.Samples">
<Model exs:type="Model">
<Name>name</Name>
</Model>
</Example>
ExtendedXmlSerializer support .net 4.5 and .net Core.
Replacing the IModelObject interface with an abstract or concrete type and use inheritance with XMLInclude is possible, but seems like an ugly workaround.
If it is possible to use an abstract base I would recommend that route. It will still be cleaner than using hand-rolled serialization. The only trouble I see with the abstract base is that your still going to need the concrete type? At least that is how I've used it in the past, something like:
public abstract class IHaveSomething
{
public abstract string Something { get; set; }
}
public class MySomething : IHaveSomething
{
string _sometext;
public override string Something
{ get { return _sometext; } set { _sometext = value; } }
}
[XmlRoot("abc")]
public class seriaized
{
[XmlElement("item", typeof(MySomething))]
public IHaveSomething data;
}
Unfortunately there's no simple answer, as the serializer doesn't know what to serialize for an interface. I found a more complete explaination on how to workaround this on MSDN
Unfortuantely for me, I had a case where the class to be serialized had properties that had interfaces as properties as well, so I needed to recursively process each property. Also, some of the interface properties were marked as [XmlIgnore], so I wanted to skip over those. I took ideas that I found on this thread and added some things to it to make it recursive. Only the deserialization code is shown here:
void main()
{
var serializer = GetDataContractSerializer<MyObjectWithCascadingInterfaces>();
using (FileStream stream = new FileStream(xmlPath, FileMode.Open))
{
XmlDictionaryReader reader = XmlDictionaryReader.CreateTextReader(stream, new XmlDictionaryReaderQuotas());
var obj = (MyObjectWithCascadingInterfaces)serializer.ReadObject(reader);
// your code here
}
}
DataContractSerializer GetDataContractSerializer<T>() where T : new()
{
Type[] types = GetTypesForInterfaces<T>();
// Filter out duplicates
Type[] result = types.ToList().Distinct().ToList().ToArray();
var obj = new T();
return new DataContractSerializer(obj.GetType(), types);
}
Type[] GetTypesForInterfaces<T>() where T : new()
{
return GetTypesForInterfaces(typeof(T));
}
Type[] GetTypesForInterfaces(Type T)
{
Type[] result = new Type[0];
var obj = Activator.CreateInstance(T);
// get the type for all interface properties that are not marked as "XmlIgnore"
Type[] types = T.GetProperties()
.Where(p => p.PropertyType.IsInterface &&
!p.GetCustomAttributes(typeof(System.Xml.Serialization.XmlIgnoreAttribute), false).Any())
.Select(p => p.GetValue(obj, null).GetType())
.ToArray();
result = result.ToList().Concat(types.ToList()).ToArray();
// do the same for each of the types identified
foreach (Type t in types)
{
Type[] embeddedTypes = GetTypesForInterfaces(t);
result = result.ToList().Concat(embeddedTypes.ToList()).ToArray();
}
return result;
}
I have found a simpler solution (you don't need the DataContractSerializer), thanks to this blog here:
XML serializing derived types when base type is in another namespace or DLL
But 2 problems can rise in this implementation:
(1) What if DerivedBase is not in the namespace of class Base, or even worse in a project that depends on Base namespace, so Base cannot XMLInclude DerivedBase
(2) What if we only have class Base as a dll ,so again Base cannot XMLInclude DerivedBase
Till now, ...
So the solution to the 2 problems is by using XmlSerializer Constructor (Type, array[]) :
XmlSerializer ser = new XmlSerializer(typeof(A), new Type[]{ typeof(DerivedBase)});
A detailed example is provided here on MSDN:
XmlSerializer Constructor (Type, extraTypesArray[])
It seems to me that for DataContracts or Soap XMLs, you need to check the XmlRoot as mentioned here in this SO question.
A similar answer is here on SO but it isn't marked as one, as it not the OP seems to have considered it already.
in my project, I have a
List<IFormatStyle> FormatStyleTemplates;
containing different Types.
I then use the solution 'XmlAnything' from above, to serialize this list of different types.
The generated xml is beautiful.
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
[XmlArray("FormatStyleTemplates")]
[XmlArrayItem("FormatStyle")]
public XmlAnything<IFormatStyle>[] FormatStyleTemplatesXML
{
get
{
return FormatStyleTemplates.Select(t => new XmlAnything<IFormatStyle>(t)).ToArray();
}
set
{
// read the values back into some new object or whatever
m_FormatStyleTemplates = new FormatStyleProvider(null, true);
value.ForEach(t => m_FormatStyleTemplates.Add(t.Value));
}
}

Serialize/Deserialize derived class as base class

For example I have the following classes:
public abstract class Device
{
}
public class WindowsDevice: Device
{
}
public class AndroidDevice: Device
{
}
Now I want to serialize/deserialize WindowsDevice and AndroidDevice as XML:
public static string Serialize(object o, Type[] additionalTypes = null)
{
var serializer = new XmlSerializer(o.GetType(), additionalTypes);
using (var stringWriter = new StringWriterWithEncoding(Encoding.UTF8))
{
serializer.Serialize(stringWriter, o);
return stringWriter.ToString();
}
}
This will produce the following output:
<?xml version="1.0" encoding="utf-8"?>
<WindowsDevice xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
</WindowsDevice>
But now I am unable to deserialize this, because in my application I don't know if the XML is WindowsDevice or AndroidDevice, so I have to deserialize as typeof(Device). But then I will get an exception that "WindowsDevice" was unexpected in the XML.
I tried XmlInclude and extraTypes without any success.
What I dont understand is, that if I have the following sample class:
public class SampleClass
{
public List<Device> Devices {get;set}
}
and if I serialize SampleClass and use XmlInclude or extraTypes I exactly get what I want:
<Devices>
<Device xsi:type="WindowsDevice"></Device>
</Devices>
But I don't have that class and I don't have a list of Devices. I only want to serialize/deserialize WindowsDevice and AndroidDevice but on Deserialize I don't know whether it is AndroidDevice or WindowsDevice so I have to use typeof(Device) and want to get the correct sublass AndroidDevice or WindowsDevice, so instead of:
<WindowsDevice></WindowsDevice>
I want to have:
<Device xsi:type="WindowsDevice"></Device>
How can this be done?
Your problem is that you are constructing your XmlSerializer inconsistently during serialization and deserialization. You need to construct it using the same Type argument in both cases, specifically the base type typeof(Device). Thus I'd suggest you replace your existing completely general serialization method with one specific for a Device:
public static class DeviceExtensions
{
public static string SerializeDevice<TDevice>(this TDevice o) where TDevice : Device
{
// Ensure that [XmlInclude(typeof(TDevice))] is present on Device.
// (Included for clarity -- actually XmlSerializer will make a similar check.)
if (!typeof(Device).GetCustomAttributes<XmlIncludeAttribute>().Any(a => a.Type == o.GetType()))
{
throw new InvalidOperationException("Unknown device type " + o.GetType());
}
var serializer = new XmlSerializer(typeof(Device)); // Serialize as the base class
using (var stringWriter = new StringWriterWithEncoding(Encoding.UTF8))
{
serializer.Serialize(stringWriter, o);
return stringWriter.ToString();
}
}
public static Device DeserializeDevice(this string xml)
{
var serial = new XmlSerializer(typeof(Device));
using (var reader = new StringReader(xml))
{
return (Device)serial.Deserialize(reader);
}
}
}
Then, apply [XmlInclude(typeof(TDevice))] to Device for all possible subtypes:
[XmlInclude(typeof(WindowsDevice))]
[XmlInclude(typeof(AndroidDevice))]
public abstract class Device
{
}
Then both types of devices can now be serialized and deserialized successfully while retaining their type, because XmlSerializer will include an "xsi:type" attribute to explicitly indicate the type:
<Device xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:type="WindowsDevice" />
Or
<Device xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:type="AndroidDevice" />
Sample fiddle.
Updates
So the issue was, that I serialized with typeof(WindowsDevice) instead of typeof(Device)?
Yes.
Any ideas for a solution which will work, if I have to use typeof(WindowsDevice)? Cause I have hundreds of classes and don't want to use hundreds of different XmlSerializer initializations...
This is more of an architectural question than a howto question. One possibility would be to introduce a custom attribute that you can apply to a class to indicate that any subtypes of that class should always be serialized as the attributed base type. All appropriate [XmlInclude(typeof(TDerivedType))] attributes will also be required:
[System.AttributeUsage(System.AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
public class XmlBaseTypeAttribute : System.Attribute
{
}
[XmlInclude(typeof(WindowsDevice))]
[XmlInclude(typeof(AndroidDevice))]
[XmlBaseType]
public abstract class Device
{
}
Then modify your universal XML serialization code to look up the type hierarchy of the object being serialized for an [XmlBaseType] attribute, and (de)serialize as that type:
public static class XmlExtensions
{
static Type GetSerializedType(this Type type)
{
var serializedType = type.BaseTypesAndSelf().Where(t => Attribute.IsDefined(t, typeof(XmlBaseTypeAttribute))).SingleOrDefault();
if (serializedType != null)
{
// Ensure that [XmlInclude(typeof(TDerived))] is present on the base type
// (Included for clarity -- actually XmlSerializer will make a similar check.)
if (!serializedType.GetCustomAttributes<XmlIncludeAttribute>().Any(a => a.Type == type))
{
throw new InvalidOperationException(string.Format("Unknown subtype {0} of type {1}", type, serializedType));
}
}
return serializedType ?? type;
}
public static string Serialize(this object o)
{
var serializer = new XmlSerializer(o.GetType().GetSerializedType());
using (var stringWriter = new StringWriterWithEncoding(Encoding.UTF8))
{
serializer.Serialize(stringWriter, o);
return stringWriter.ToString();
}
}
public static T Deserialize<T>(this string xml)
{
var serial = new XmlSerializer(typeof(T).GetSerializedType());
using (var reader = new StringReader(xml))
{
return (T)serial.Deserialize(reader);
}
}
}
Of course this means that if your code tries to deserialize XML it expects to contain a WindowsDevice, it might actually get back an AndroidDevice depending upon the contents of the XML.
Sample fiddle #2.

How to implement Serialize on collection that contain interface? [duplicate]

I would like to XML serialize an object that has (among other) a property of type IModelObject (which is an interface).
public class Example
{
public IModelObject Model { get; set; }
}
When I try to serialize an object of this class, I receive the following error:
"Cannot serialize member Example.Model of type Example because it is an interface."
I understand that the problem is that an interface cannot be serialized. However, the concrete Model object type is unknown until runtime.
Replacing the IModelObject interface with an abstract or concrete type and use inheritance with XMLInclude is possible, but seems like an ugly workaround.
Any suggestions?
This is simply an inherent limitation of declarative serialization where type information is not embedded within the output.
On trying to convert <Flibble Foo="10" /> back into
public class Flibble { public object Foo { get; set; } }
How does the serializer know whether it should be an int, a string, a double (or something else)...
To make this work you have several options but if you truly don't know till runtime the easiest way to do this is likely to be using the XmlAttributeOverrides.
Sadly this will only work with base classes, not interfaces. The best you can do there is to ignore the property which isn't sufficient for your needs.
If you really must stay with interfaces you have three real options:
Hide it and deal with it in another property
Ugly, unpleasant boiler plate and much repetition but most consumers of the class will not have to deal with the problem:
[XmlIgnore()]
public object Foo { get; set; }
[XmlElement("Foo")]
[EditorVisibile(EditorVisibility.Advanced)]
public string FooSerialized
{
get { /* code here to convert any type in Foo to string */ }
set { /* code to parse out serialized value and make Foo an instance of the proper type*/ }
}
This is likely to become a maintenance nightmare...
Implement IXmlSerializable
Similar to the first option in that you take full control of things but
Pros
You don't have nasty 'fake' properties hanging around.
you can interact directly with the xml structure adding flexibility/versioning
Cons
you may end up having to re-implement the wheel for all the other properties on the class
Issues of duplication of effort are similar to the first.
Modify your property to use a wrapping type
public sealed class XmlAnything<T> : IXmlSerializable
{
public XmlAnything() {}
public XmlAnything(T t) { this.Value = t;}
public T Value {get; set;}
public void WriteXml (XmlWriter writer)
{
if (Value == null)
{
writer.WriteAttributeString("type", "null");
return;
}
Type type = this.Value.GetType();
XmlSerializer serializer = new XmlSerializer(type);
writer.WriteAttributeString("type", type.AssemblyQualifiedName);
serializer.Serialize(writer, this.Value);
}
public void ReadXml(XmlReader reader)
{
if(!reader.HasAttributes)
throw new FormatException("expected a type attribute!");
string type = reader.GetAttribute("type");
reader.Read(); // consume the value
if (type == "null")
return;// leave T at default value
XmlSerializer serializer = new XmlSerializer(Type.GetType(type));
this.Value = (T)serializer.Deserialize(reader);
reader.ReadEndElement();
}
public XmlSchema GetSchema() { return(null); }
}
Using this would involve something like (in project P):
public namespace P
{
public interface IFoo {}
public class RealFoo : IFoo { public int X; }
public class OtherFoo : IFoo { public double X; }
public class Flibble
{
public XmlAnything<IFoo> Foo;
}
public static void Main(string[] args)
{
var x = new Flibble();
x.Foo = new XmlAnything<IFoo>(new RealFoo());
var s = new XmlSerializer(typeof(Flibble));
var sw = new StringWriter();
s.Serialize(sw, x);
Console.WriteLine(sw);
}
}
which gives you:
<?xml version="1.0" encoding="utf-16"?>
<MainClass
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Foo type="P.RealFoo, P, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
<RealFoo>
<X>0</X>
</RealFoo>
</Foo>
</MainClass>
This is obviously more cumbersome for users of the class though avoids much boiler plate.
A happy medium may be merging the XmlAnything idea into the 'backing' property of the first technique. In this way most of the grunt work is done for you but consumers of the class suffer no impact beyond confusion with introspection.
The solution to this is using reflection with the DataContractSerializer. You don't even have to mark your class with [DataContract] or [DataMember]. It will serialize any object, regardless of whether it has interface type properties (including dictionaries) into xml. Here is a simple extension method that will serialize any object into XML even if it has interfaces (note you could tweak this to run recursively as well).
public static XElement ToXML(this object o)
{
Type t = o.GetType();
Type[] extraTypes = t.GetProperties()
.Where(p => p.PropertyType.IsInterface)
.Select(p => p.GetValue(o, null).GetType())
.ToArray();
DataContractSerializer serializer = new DataContractSerializer(t, extraTypes);
StringWriter sw = new StringWriter();
XmlTextWriter xw = new XmlTextWriter(sw);
serializer.WriteObject(xw, o);
return XElement.Parse(sw.ToString());
}
what the LINQ expression does is it enumerates each property,
returns each property that is an interface,
gets the value of that property (the underlying object),
gets the type of that concrete object
puts it into an array, and adds that to the serializer's list of known types.
Now the serializer knows how about the types it is serializing so it can do its job.
If you know your interface implementors up-front there's a fairly simple hack you can use to get your interface type to serialize without writing any parsing code:
public interface IInterface {}
public class KnownImplementor01 : IInterface {}
public class KnownImplementor02 : IInterface {}
public class KnownImplementor03 : IInterface {}
public class ToSerialize {
[XmlIgnore]
public IInterface InterfaceProperty { get; set; }
[XmlArray("interface")]
[XmlArrayItem("ofTypeKnownImplementor01", typeof(KnownImplementor01))]
[XmlArrayItem("ofTypeKnownImplementor02", typeof(KnownImplementor02))]
[XmlArrayItem("ofTypeKnownImplementor03", typeof(KnownImplementor03))]
public object[] InterfacePropertySerialization {
get { return new[] { InterfaceProperty }; ; }
set { InterfaceProperty = (IInterface)value.Single(); }
}
}
The resulting xml should look something along the lines of
<interface><ofTypeKnownImplementor01><!-- etc... -->
You can use ExtendedXmlSerializer. This serializer support serialization of interface property without any tricks.
var serializer = new ConfigurationContainer().UseOptimizedNamespaces().Create();
var obj = new Example
{
Model = new Model { Name = "name" }
};
var xml = serializer.Serialize(obj);
Your xml will look like:
<?xml version="1.0" encoding="utf-8"?>
<Example xmlns:exs="https://extendedxmlserializer.github.io/v2" xmlns="clr-namespace:ExtendedXmlSerializer.Samples.Simple;assembly=ExtendedXmlSerializer.Samples">
<Model exs:type="Model">
<Name>name</Name>
</Model>
</Example>
ExtendedXmlSerializer support .net 4.5 and .net Core.
Replacing the IModelObject interface with an abstract or concrete type and use inheritance with XMLInclude is possible, but seems like an ugly workaround.
If it is possible to use an abstract base I would recommend that route. It will still be cleaner than using hand-rolled serialization. The only trouble I see with the abstract base is that your still going to need the concrete type? At least that is how I've used it in the past, something like:
public abstract class IHaveSomething
{
public abstract string Something { get; set; }
}
public class MySomething : IHaveSomething
{
string _sometext;
public override string Something
{ get { return _sometext; } set { _sometext = value; } }
}
[XmlRoot("abc")]
public class seriaized
{
[XmlElement("item", typeof(MySomething))]
public IHaveSomething data;
}
Unfortunately there's no simple answer, as the serializer doesn't know what to serialize for an interface. I found a more complete explaination on how to workaround this on MSDN
Unfortuantely for me, I had a case where the class to be serialized had properties that had interfaces as properties as well, so I needed to recursively process each property. Also, some of the interface properties were marked as [XmlIgnore], so I wanted to skip over those. I took ideas that I found on this thread and added some things to it to make it recursive. Only the deserialization code is shown here:
void main()
{
var serializer = GetDataContractSerializer<MyObjectWithCascadingInterfaces>();
using (FileStream stream = new FileStream(xmlPath, FileMode.Open))
{
XmlDictionaryReader reader = XmlDictionaryReader.CreateTextReader(stream, new XmlDictionaryReaderQuotas());
var obj = (MyObjectWithCascadingInterfaces)serializer.ReadObject(reader);
// your code here
}
}
DataContractSerializer GetDataContractSerializer<T>() where T : new()
{
Type[] types = GetTypesForInterfaces<T>();
// Filter out duplicates
Type[] result = types.ToList().Distinct().ToList().ToArray();
var obj = new T();
return new DataContractSerializer(obj.GetType(), types);
}
Type[] GetTypesForInterfaces<T>() where T : new()
{
return GetTypesForInterfaces(typeof(T));
}
Type[] GetTypesForInterfaces(Type T)
{
Type[] result = new Type[0];
var obj = Activator.CreateInstance(T);
// get the type for all interface properties that are not marked as "XmlIgnore"
Type[] types = T.GetProperties()
.Where(p => p.PropertyType.IsInterface &&
!p.GetCustomAttributes(typeof(System.Xml.Serialization.XmlIgnoreAttribute), false).Any())
.Select(p => p.GetValue(obj, null).GetType())
.ToArray();
result = result.ToList().Concat(types.ToList()).ToArray();
// do the same for each of the types identified
foreach (Type t in types)
{
Type[] embeddedTypes = GetTypesForInterfaces(t);
result = result.ToList().Concat(embeddedTypes.ToList()).ToArray();
}
return result;
}
I have found a simpler solution (you don't need the DataContractSerializer), thanks to this blog here:
XML serializing derived types when base type is in another namespace or DLL
But 2 problems can rise in this implementation:
(1) What if DerivedBase is not in the namespace of class Base, or even worse in a project that depends on Base namespace, so Base cannot XMLInclude DerivedBase
(2) What if we only have class Base as a dll ,so again Base cannot XMLInclude DerivedBase
Till now, ...
So the solution to the 2 problems is by using XmlSerializer Constructor (Type, array[]) :
XmlSerializer ser = new XmlSerializer(typeof(A), new Type[]{ typeof(DerivedBase)});
A detailed example is provided here on MSDN:
XmlSerializer Constructor (Type, extraTypesArray[])
It seems to me that for DataContracts or Soap XMLs, you need to check the XmlRoot as mentioned here in this SO question.
A similar answer is here on SO but it isn't marked as one, as it not the OP seems to have considered it already.
in my project, I have a
List<IFormatStyle> FormatStyleTemplates;
containing different Types.
I then use the solution 'XmlAnything' from above, to serialize this list of different types.
The generated xml is beautiful.
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
[XmlArray("FormatStyleTemplates")]
[XmlArrayItem("FormatStyle")]
public XmlAnything<IFormatStyle>[] FormatStyleTemplatesXML
{
get
{
return FormatStyleTemplates.Select(t => new XmlAnything<IFormatStyle>(t)).ToArray();
}
set
{
// read the values back into some new object or whatever
m_FormatStyleTemplates = new FormatStyleProvider(null, true);
value.ForEach(t => m_FormatStyleTemplates.Add(t.Value));
}
}

How to serialize IEnumerable<char> as string & Confusing Deserialization error

Lets say I have the following simple class:
[XmlRoot]
[XmlType("string")]
public partial class eString : IEnumerable<char>
{
string AsString {get;set;}
public IEnumerator<char> GetEnumerator()
{
return this.AsString.ToCharArray().ToList().GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.AsString.ToCharArray().GetEnumerator();
}
public void Add(char character)
{
this.AsString += character;
}
}
Also a namedValue class:
[XmlRoot]
public partial class eNamedValue: KeyValuePair<eString,object>
{...}
And finally,
[XmlRoot]
public partial class eList<T>: List<eNamedValue>
{...}
Now, I serialize eList or any class that inherits from eList using the following XML serializer:
public static XDocument Serialize<T>(this T obj) where T: new()
{
Type tt = obj.GetType();
XmlSerializer xsSubmit = new XmlSerializer(tt);
StringWriter sww = new StringWriter();
XmlWriter writer = XmlWriter.Create(sww);
xsSubmit.Serialize(writer, obj);
return XDocument.Parse(sww.ToString());
}
Serialization works - but my eString is being serialized as a character array, so instead of getting "string", I get individual characters values:
<eNamedList>
<Key>99</Key>
<Key>111</Key>
<Key>100</Key>
<Key>101</Key>...
Also, on Deserialization (SOLVED, see Update#1 below):
public static T Deserialize<T>(this XDocument xml) where T: new()
{
var serializer = new XmlSerializer(typeof(T));
T result;
using (TextReader reader = new StringReader(xml.ToString()))
{
result = (T)serializer.Deserialize(reader);
}
return result;
}
I receive the following error:
System.Runtime.Serialization.SerializationException: Error in line 1 position 111. Expecting element 'eNamedList' from namespace 'http://schemas.datacontract.org/2004/07/Epic'.. Encountered 'Element' with name 'eNamedList', namespace ''.
So, my questions become:
How do I control serialization of my eString, or any IEnumerable<char> and always serialize as a string?
Why, when the element names match, do I get a failure on deserialization? (Am I just missing a namespace declaration?)
Thanks!
Update #1:
So, I removed the IEnumerable<char> interface from my eString, and just left the IEnumerable<char>.GetEnumerator() method, which allows my string to be used AS an IEnumerable<char> while in a foreach loop, but serializes as a string. #WIN
Also, thanks to dbc, updated the original post with the XML Deserializer (rather than DataContract Serializer) and deserialization works.
To answer your questions:
You probably shouldn't reinvent the wheel with custom string solutions. Regardless, if you want an (insanely) high-level of control over your (de-)serialization, you could implement IXmlSerializable and do the exact same thing yourself.
[XmlRoot("string-wrapper")]
public class CustomString : IXmlSerializable
{
public string Value { get; set; }
public XmlSchema GetSchema()
{
return null; // GetSchema should not be used.
}
public void ReadXml(XmlReader reader)
{
reader.MoveToContent();
bool isEmpty = reader.IsEmptyElement;
reader.ReadStartElement();
if (!isEmpty)
{
Value = reader.ReadString();
reader.ReadEndElement();
}
}
public void WriteXml(XmlWriter writer)
{
writer.WriteString(Value);
}
}
Serialization of a CustomString now yields <string-wrapper>Testing</string-wrapper>. I'll post some test code at the end of this post.
Your deserialization is likely broken because the XmlSerializer doesn't know that the IEnumerable you marked serializable should actually be treated like a string.
And now... Forget what I just told you. Unless you have very specific formatting requirements you should not implement your own version of a string. The built-in formatter knows a lot more formatting tricks (http://www.w3.org/TR/xmlschema-2/#built-in-datatypes), so you should probably let it do it's job.
Serializing classes is where the attributes come in, though I recommend you switch to the WFC data contracts, which may sound scary at first but actually provides a whole lot more for a lot less code. Again, I'm not sure what you're trying to accomplish, but trust me you don't want to get into the habit of hand writing XML.
If you're up for it you might like dynamic objects and the ExpandoObject (http://www.codeproject.com/Tips/227139/Converting-XML-to-an-dynamic-object-using-ExpandoO). These eliminate types all together and allow you to create dictionaries, arrays, named properties, whatever, all on the fly!
Finally, easy on the generics! Deserializing generic classes is not a trivial task. Besides, you probably don't need to. If you want your own collections, try the System.Collections.ObjectModel namespace. You don't have to worry about maintaining lists and implementing interfaces, just what you're actually storing:
class DictionaryOfStringsAndObjects : KeyedCollection<string, object {...}
class CollectionOfStrings : Collection<string> {...}
Also, try to avoid partial classes unless an ORM or a large legacy class forces it on you. You shouldn't actually use them unless you're made to.
All the best, and to a future devoid of XML!
public class CustomSerializer
{
public static void Test()
{
var obj = new CustomString {Value = "Random string!"};
var serializer = new CustomSerializer();
var xml = serializer.Serialize(obj);
Console.WriteLine(xml);
var obj2 = serializer.Deserialize<CustomString>(xml);
}
public string Serialize(object obj)
{
var serializer = new XmlSerializer(obj.GetType());
using (var io = new StringWriter())
{
serializer.Serialize(io, obj);
return io.ToString();
}
}
public T Deserialize<T>(string xml)
{
var serializer = new XmlSerializer(typeof (T));
using (var io = new StringReader(xml))
{
return (T)serializer.Deserialize(io);
}
}
}

How to serialize multiple members of "this"

I have a class with a couple of members I want to serialise to store state.
However, I want to serialise from WITHIN the class itself, not via some external class feeding it to a formatter.
So in theory I want to do something like:
[DataContract]
class MyClass
{
[DataMember]
private MyCompoundClass _someCompoundField;
[DataMember]
private int _someOtherField;
private void SaveState()
{
using (Stream stream = GetStream())
{
DataContractSerializer serialiser = new DataContractSerializer(typeof(MyClass));
serialiser.WriteObject(stream, this);
}
}
private void LoadState()
{
using (Stream stream = GetStream())
{
DataContractSerializer serialiser = new DataContractSerializer(typeof(MyClass));
this = (MyClass)serialiser.ReadObject(stream);
}
}
}
Now obviously the line
this = (MyClass)serialiser.ReadObject(stream);
is nonsense, but you can see what I'm trying to do. I want to serialise the two fields of my class from within the class. (I am using the WCF serializer, but I assume this will be the same if I use XmlSerializer).
I tried to implement this properly by serialising each field myself like so:
private void SaveState()
{
using (Stream stream = GetStream())
{
//serialise field 1
DataContractSerializer serialiser = new DataContractSerializer(typeof(MyCompoundClass));
serialiser.WriteObject(stream, _someCompoundField);
//serialise field 2
serialiser = new DataContractSerializer(typeof(int));
serialiser.WriteObject(stream, _someOtherField);
}
}
Now this works as a save, but when I come to read the document back in it throws an exception since there are two root nodes in the XML file.
How do I create my "wrapper" node to wrap my fields. Or is there some other way I should be doing this?
Many thanks,
I haven't seen any built-in deserialisation methods which modify an existing object, rather than returning a new one. Your options as I see them are:
Deserialise a new MyClass and copy the members over
Make LoadState static and have it return the deserialised MyClass
Use a different serialisation mechanism which can do what you want
you could at your LoadState do:
private void LoadState()
{
using (Stream stream = GetStream())
{
DataContractSerializer serialiser = new DataContractSerializer(typeof(MyClass));
MyClass deserialized = (MyClass)serialiser.ReadObject(stream);
this._someCompoundField = deserialized._someCompoundField;
this._someOtherField = deserialized._someOtherField;
}
}
Are you tied to a specific serializer? protobuf-net supports that use-case, for example:
[DataContract]
class MyClass
{
[DataMember(Order = 1)]
private MyCompoundClass _someCompoundField;
[DataMember(Order = 2)]
private int _someOtherField;
private void SaveState()
{
using (Stream stream = GetStream())
{
ProtoBuf.Serializer.Serialize(stream, this);
}
}
private void LoadState()
{
using (Stream stream = GetStream())
{
ProtoBuf.Serializer.Merge(stream, this);
}
}
}
Note the addition of Order = n on the member-attributes; that is because protobuf uses numeric identifiers on fields/properties, and needs a way to choose them. You can also use the project-specific [ProtoContract]/[ProtoMember(n)] attributes, but it works with the WCF ones too (as shown).
(Merge is also available on the non-generic 2.0 API - but you pass this in as an argument to Deseroalize instead)
Put your deserialization method in a static method:
class MyClass
{
public static MyClass LoadState()
{
// Deserialize, and return the new MyClass instance.
}
}
To serialize just some objects, annotate some fields to avoid serialization, or create a superclass or interface with just the fields in it you want to serialize.

Categories