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);
}
}
}
Related
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));
}
}
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));
}
}
When I try to deserialized my FooContainer (List) throws me an error. Please, just watch the last part of this code.
public interface IFoo
{
object Value { get; }
}
public abstract class Foo<T> : IFoo
{
[XmlElement("Value")]
public T Value { get; set; }
[XmlIgnore]
object IFoo.Value { get { return Value; } }
}
public class FooA : Foo<string> { }
public class FooB : Foo<int> { }
public class FooC : Foo<List<Double>> { }
[XmlRoot("Foos")]
public class FooContainer : List<IFoo>, IXmlSerializable
{
public XmlSchema GetSchema()
{
throw new NotImplementedException();
}
public void ReadXml(XmlReader reader)
{
XmlSerializer serial = new XmlSerializer(typeof(FooContainer));
serial.Deserialize(reader);
}
public void WriteXml(XmlWriter writer)
{
ForEach(x =>
{
XmlSerializer serial = new XmlSerializer(x.GetType(),
new Type[] { typeof(FooA), typeof(FooB), typeof(FooC) });
serial.Serialize(writer, x);
});
}
}
class Program
{
static void Main(string[] args)
{
FooContainer fooList = new FooContainer()
{
new FooA() { Value = "String" },
new FooB() { Value = 2 },
new FooC() { Value = new List<double>() {2, 3.4 } }
};
XmlSerializer serializer = new XmlSerializer(fooList.GetType(),
new Type[] { typeof(FooA), typeof(FooB), typeof(FooC) });
System.IO.TextWriter textWriter = new System.IO.StreamWriter(#"C:\temp\movie.xml");
serializer.Serialize(textWriter, fooList);
textWriter.Close();
XmlReader reader = XmlReader.Create(#"C:\temp\movie.xml");
var a = serializer.Deserialize(reader);
}
}
What am I doing wrong?
I had the exact same Problem:
To me it seems that you are missing set in your interface IFoo or class Foo. Without set the object becomes readonly and you will receive a StackOverflowException when trying to deserialize XML to Object.
I simply added set to my attributes, and the thing solved itself.
You don't seem to understand what IXmlSerializable is for. It's meant for overriding the default behaviour of an XmlSerializer. If you just spin up another XmlSerializer to actually serialize/deserialize in your WriteXml/ReadXml methods then yes, you will get a StackOverflowException because that serializer is going to call the exact same WriteXml/ReadXml/ method it came from on exactly the same object.
Just get rid of that IXmlSerializable implementation, since it doesn't actually do anything. Or read this CP article:
How to Implement IXmlSerializable Correctly
Basically, you are supposed to use the standard Read* and Write* methods on XmlReader or XmlWriter to read or write the object.
It's not necessarily always wrong to use an XmlSerializer inside a ReadXml or WriteXml method - as long as you're using it to serialize/deserialize a different object, one which neither equals nor contains an instance of the declaring type in the object graph.
I'd debug it to make sure, but my guess is your implementation of ReadXml is recursive. ReadXml calls Deserialize, which calls ReadXml, which calls Deserialize, etc.
ReadXml should read the xml using the reader that's passed in and hydrate the object (i.e. this) based on the Xml data.
from MSDN
The ReadXml method must reconstitute your object using the information
that was written by the WriteXml method.
When this method is called, the reader is positioned at the start of
the element that wraps the information for your type. That is, just
before the start tag that indicates the beginning of a serialized
object. When this method returns, it must have read the entire element
from beginning to end, including all of its contents. Unlike the
WriteXml method, the framework does not handle the wrapper element
automatically. Your implementation must do so. Failing to observe
these positioning rules may cause code to generate unexpected runtime
exceptions or corrupt data.
When implementing this method, you should consider the possibility
that a malicious user might provide a well-formed but invalid XML
representation in order to disable or otherwise alter the behavior of
your application.
I have the following class and data members (plus irrelevant methods) I am new to XML and .NET (that excuse is getting old though) and have spent a couple of days reading the MSDN entries (and whatever google turned up) for first XMLReader, then XMLDocument and now XDocument, XElement and XNode but am no closer to a concrete solution to serializing my class (perhaps I need to study serialization in .NET in more depth). To get me started I have some data in an XML file that I want to read (although it prolly is in the wrong format) in to initialize a class to initialize my application. The configuration class is as follows:
class IWantToFile
{
class DirsAndFiles
{
public List<string> Suffixes;
public string Dir;
}
enum OPOptsEnum
{
op1Description, op2Description, op3Description, op4Description,
op5Description, op6Description, op7Description, op8Description,
};
List<DirsAndFiles> ProjectDirs;
bool[] OPOpts = new bool[(int)OPOptsEnum.op8Description + 1];
bool otherOpt;
}
Observing the one to one and one to many relationships therein (eg List<DirsAndFiles> ProjectDirs) can someone please give concise methods to read and write this data to a file? It would greatly assist my development in these fields.
I've got as far as:
if (File.Exists(SPECFILENAME)) {
XDocument xdoc = XDocument.Load(SPECFILENAME);
//Ummm.....
}
but then my lack of .NET XML and Linq exeperience fail me.
I think you might want to use the XmlSerializer, which 'Serializes and deserializes objects into and from XML documents'?
See How to serialize an object to XML by using Visual C# for sample code.
[Serializable]
public class MyObject
{
public string SerializeMe { get; set; }
[XmlIgnore]
public string DONTSerializeMe { get; set; }
}
Helper....
public static class SerializerHelper<T>
{
public static string Serialize(T myobject)
{
XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
StringWriter stringWriter = new StringWriter();
xmlSerializer.Serialize(stringWriter, myobject);
string xml = stringWriter.ToString();
return xml;
}
public static T Deserialize(string xml)
{
XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
StringReader stringReader = new StringReader(xml);
return (T)xmlSerializer.Deserialize(stringReader);
}
}
Usage
MyObject myObject = new MyObject();
string xml = SerializerHelper<MyObject>.Serialize(myObject);
MyObject DeserializedObject = SerializerHelper<MyObject>.Deserialize(xml);
I am using C# + VSTS2008 + .Net 3.0 to do XML serialization. The code works fine. Here below is my code and current serialized XML results.
Now I want to add two additional layers to the output XML file. Here is my expected XML results. Any easy way to do this? I am not sure whether NestingLevel could help to do this. I want to find an easy way which does not change the structure of MyClass and MyObject.
Expected XML serialization result,
<?xml version="1.0"?>
<MyClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<MyObjectProperty>
<AdditionalLayer1>
<AdditionalLayer2>
<ObjectName>Foo</ObjectName>
</AdditionalLayer1>
</AdditionalLayer2>
</MyObjectProperty>
</MyClass>
Current XML serialization result,
<?xml version="1.0"?>
<MyClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<MyObjectProperty>
<ObjectName>Foo</ObjectName>
</MyObjectProperty>
</MyClass>
My current code,
public class MyClass
{
public MyObject MyObjectProperty;
}
public class MyObject
{
public string ObjectName;
}
public class Program
{
static void Main(string[] args)
{
XmlSerializer s = new XmlSerializer(typeof(MyClass));
FileStream fs = new FileStream("foo.xml", FileMode.Create);
MyClass instance = new MyClass();
instance.MyObjectProperty = new MyObject();
instance.MyObjectProperty.ObjectName = "Foo";
s.Serialize(fs, instance);
return;
}
}
I want to find an easy way which does not change the structure of MyClass and MyObject.
Frankly, that isn't going to happen. XmlSerializer (when used simply) follows the structure of the classes / members. So you can't add extra layers without changing the structure.
When used in a bespoke way (IXmlSerializable), the one thing you can say for sure is that it isn't simple... this is not a fun interface to implement.
My advice: introduce a DTO that mimics your desired format, and shim to that before you serialize.
Why do you need these additional two layers?
Is its a business requirement, you should add these two objects to your object model:
public class MyClass
{
public AdditionalLayer1 AdditionalLayer1;
}
public class AdditionalLayer1
{
public AdditionalLayer2 AdditionalLayer2;
}
public class AdditionalLayer2
{
public MyObject MyObjectProperty;
}
public class MyObject
{
public string ObjectName;
}
if its purely because of some compatibility requirements you can either do the thing above, or implement IXmlSerializable on MyClass:
public class MyClass : IXmlSerializable
{
public MyObject MyObjectProperty;
public void WriteXml (XmlWriter writer)
{
//open the two layers
//serialize MyObject
//close the two layers
}
public void ReadXml (XmlReader reader)
{
//read all the layers back, etc
}
public XmlSchema GetSchema()
{
return(null);
}
}
You could do a transformation with XSL after serializing and add the "missing" tags.
I would suggest serializing out to a MemoryStream instead of a FileStream and then loading the XML into an XmlDocument, then using the methods of that class to insert the extra nodes you want. After that, you can save it out.
I did exactly this only recently and didn't find overriding the IXmlSerializable interface at all difficult or complicated. Serialize/deserialize as normal but for the class that contains the sub-class which you need to wrap in extra tags derive from IXmlSerializable:
class ContainingClass : IXmlSerializable
{
...
#region IXmlSerializable Members
public XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader)
{
**reader.ReadStartElement("Equipment");**
XmlSerializer ser = new XmlSerializer(typeof(Host));
while (reader.NodeType != XmlNodeType.EndElement)
{
Host newHost = new Host();
newHost.Name = (string) ser.Deserialize(reader);
_hosts.Add(newHost);
reader.Read();
}
// Read the node to its end.
// Next call of the reader methods will crash if not called.
**reader.ReadEndElement(); // "Equipment"**
}
public void WriteXml(XmlWriter writer)
{
XmlSerializer ser = new XmlSerializer(typeof(Host));
**writer.WriteStartElement("Equipment");**
foreach (Host host in this._hosts)
{
ser.Serialize(writer, host);
}
**writer.WriteEndElement();**
}
#endregion
All I do here is to wrap the de/serialization of the lower level objects. Here I'm wrapping an extra tag "Equipment" around all the Hosts in a collection in this class. You could add as many tags as you like here as long as you close each one you start.
NOTE: Bold is showing as ** text ** - just make sure you get rid of the double asterisks :)