XML serialization results in duplicate nodes - c#

I have an object structure that I'm trying to serialize to xml that's resulting in a duplicated node level. I'm pretty sure it has something to do with subclassing because I had to implement my own deserialization but I'm not sure exactly what's going on in the other direction. The same xml structure is used as input when deserializing my data at application launch as when reserializing it to be saved later.
Here's what the faulty output xml looks like:
<Keyboarding xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Exercises>
<Exercise>
<Exercise Id="3" ActivityNumber="5" SubActivityNumber="b" Type="Standard">
<Title>Test Title</Title>
<Instructions>Downloaded Update Instructions</Instructions>
</Exercise>
</Exercise>
</Exercises>
</Keyboarding>
Here's what it should look like (and looks like on initial deserialization):
<Keyboarding xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Exercises>
<Exercise Id="3" ActivityNumber="5" SubActivityNumber="b" Type="Standard">
<Title>Test Title</Title>
<Instructions>Downloaded Update Instructions</Instructions>
</Exercise>
</Exercises>
</Keyboarding>
The root object contains a collection of exercises. Exercise is my base class, while subclasses are determined by the Type attribute. I'm using a custom xml serializer to build the objects of varying derived types from Exercise because it allows me to use reflection to match any of the 2 dozen or so potential derived types, which are in an unknown order and quantity when first read in by my application.
Here's the root element's collection, using the custom xml serializer:
[XmlArray("Exercises")]
[XmlArrayItem("Exercise", Type = typeof (ExerciseXmlSerializer<Exercise>))]
public Collection<Exercise> UnprocessedExercises { get; set; }
My base exercise class is declared as follows:
[Serializable]
[XmlType(AnonymousType = true)]
public class Exercise
And my derived class is declared as follows:
[Serializable]
[XmlType(AnonymousType = true)]
[XmlRoot(ElementName = "Exercise", IsNullable = false)]
public class StandardExercise : Exercise
Here's the writer portion of my custom xml serializer:
public class ExerciseXmlSerializer<T> : IXmlSerializable where T : class
{
private T _data;
...
public void WriteXml(XmlWriter writer)
{
Type type = _data.GetType();
new XmlSerializer(type).Serialize(writer, _data);
}
...
}
In the WriteXml() method, the type variable is properly set to the derived type, so why does it create a node level for both the base type and the derived type? How do I prevent it from doing so?

I think your problem is with this line:
[XmlArrayItem("Exercise", Type = typeof (ExerciseXmlSerializer<Exercise>))]
I don't think that you want to indicate that the type of the items are a type of the serializer. If you want to implement IXmlSerializable, it needs to be on the class to be serialized.
Here is what I got to work:
public class Keyboarding
{
[XmlArray("Exercises")]
[XmlArrayItem("Exercise")]
public Collection<Exercise> UnprocessedExercises { get; set; }
public Keyboarding()
{
UnprocessedExercises = new Collection<Exercise>();
UnprocessedExercises.Add(new StandardExercise());
}
}
[XmlInclude(typeof(StandardExercise))]
public class Exercise
{
}
[Serializable]
public class StandardExercise : Exercise
{
}
This produces output similar to the following:
<?xml version=\"1.0\"?>
<Keyboarding xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">
<Exercises>
<Exercise xsi:type=\"StandardExercise\" />
</Exercises>
</Keyboarding>
If you don't want the xsi:type included in the output, you can use the answer to this question.

You should be able to use XmlAttributeOverrides to do what you're asking. In this case, you'd probably do something like this. The sample code is a little rough because you didn't include your entire object graph, but hopefully this will point you in the right direction.
List<Type> extraTypes = new List<Type>();
XmlAttributes attrs = new XmlAttributes();
extraTypes.Add(typeof(Exercise));
extraTypes.Add(typeof(StandardExercise));
XmlElementAttribute attr = new XmlElementAttribute
{
ElementName = "Exercise",
Type = typeof(Exercise)
};
attrs.XmlElements.Add(attr);
attr = new XmlElementAttribute
{
ElementName = "StandardExercise",
Type = typeof(StandardExercise)
};
attrs.XmlElements.Add(attr);
XmlAttributeOverrides overrides = new XmlAttributeOverrides();
overrides.Add(typeof(Keyboarding), "Keboarding", attrs);
return new XmlSerializer(typeof(Keyboarding), overrides, extraTypes.ToArray(),
new XmlRootAttribute("Keyboarding"), string.Empty);
This should allow you to create an XML tree that looks like this. It's not quite what you were looking for, but is just as descriptive.
<Keyboarding xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Exercises>
<StandardExercise Id="3" ActivityNumber="5" SubActivityNumber="b">
<Title>Test Title</Title>
<Instructions>Downloaded Update Instructions</Instructions>
</StandardExercise >
</Exercises>
</Keyboarding>

Related

Can the serializer be forced to not generate the xsi:type attribute?

When serializing an object with a polymorphic member, the xml element representing this member automatically receives the xsi:type attribute specifying the name of the actual type.
In theory, this can help in deserialization, but unfortunately it is often the opposite, as mentioned here (question is unrelated, take a look at the JaedenRuiner's answer): XmlSerializer replace xsi:type to node name
In my case, I need to send my serialized object to an external application that has a completely different architecture and the type name I use can bring nothing but confusion there.
Sample classes:
public class Foo
{
public Bar Bar { get; set; }
}
public abstract class Bar
{ }
public class Baz : Bar
{ }
and serialization:
public void FooTest()
{
var foo = new Foo { Bar = new Baz() };
var xDoc = new XDocument();
XmlSerializer objSerializer = new XmlSerializer(typeof(Foo), new System.Type[] { typeof(Baz) });
using (XmlWriter writer = xDoc.CreateWriter())
{
objSerializer.Serialize(writer, foo);
}
Console.WriteLine(xDoc.Root);
Console.ReadKey();
}
Result:
<Foo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Bar xsi:type="Baz" />
</Foo>
Can I somehow stop generating of this xsi:type="Baz" attribute?

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));
}
}

xsi and xsd namespaces missing when using custom IXmlSerializable

I am encountering the following problem. Whenever I use the default XML serialization in my C# class, the namespaces xsi and xsd are automatically added by the .NET serialization engine. However when the serialization is defined through IXmlSerializable, the namespaces are not added.
Example: this code:
class Program
{
static void Main(string[] args)
{
OutputSerialized(new Outer() { Inner = new Inner() });
OutputSerialized(new OuterCustom() { Inner = new Inner() });
}
static void OutputSerialized<T>(T t)
{
var sb = new StringBuilder();
using (var textwriter = new StringWriter(sb))
new XmlSerializer(typeof(T)).Serialize(textwriter, t);
Console.WriteLine(sb.ToString());
}
}
[Serializable] public class Inner { }
[Serializable] public class Outer { public Inner Inner { get; set; } }
public class OuterCustom : IXmlSerializable
{
public Inner Inner;
public void WriteXml(System.Xml.XmlWriter writer)
{
writer.WriteStartElement("Inner");
new XmlSerializer(typeof(Inner)).Serialize(writer, Inner);
writer.WriteEndElement();
}
public System.Xml.Schema.XmlSchema GetSchema() { return null; }
public void ReadXml(System.Xml.XmlReader reader) { /**/ }
}
produces the following output:
<?xml version="1.0" encoding="utf-16"?>
<Outer xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Inner />
</Outer>
<?xml version="1.0" encoding="utf-16"?>
<OuterCustom>
<Inner>
<Inner xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" />
</Inner>
</OuterCustom>
You can see that the OuterCustom's serialized form is missing the xsd and xsi namespaces.
How can I make my OuterCustom behave the same way as Outer? Am I missing something in my code? Am I doing the custom serialization wrong?
There are a lot of questions here on SO about how to get rid of the additional namespaces, but it seems that no one asked about how to get them back.
First of all - there is no difference between mentioned versions of XML for compliant XML parser. It does not really matter if/how/where namespace prefixes defined as long as nodes have correct namespaces associated.
As this question is explicitly about style where to put namespace declaration one makes own call what is nice and what is not. In this particular case goal is to avoid duplicated xmlns attributes and move them higher in the tree.
One can write xmlns attributes wherever needed as long they don't conflict on the same node. Use XmlWriter.WriteAttributeString method to add them:
writer.WriteAttributeString(
"xmlns", "prefix", null, "urn:mynamespace");
To avoid duplicate declarations - check if prefix already defined for given namespace using XmlWriter.LookupPrefix
Sample code to ensure xsd and xsi prefixes (by Vlad):
const string xsiNamespace = System.Xml.Schema.XmlSchema.InstanceNamespace;
const string xsdNamespace = System.Xml.Schema.XmlSchema.Namespace;
public static void EnsureDefaultNamespaces(System.Xml.XmlWriter writer)
{
if (writer.LookupPrefix(xsiNamespace) == null)
writer.WriteAttributeString("xmlns", "xsi", null, xsiNamespace);
if (writer.LookupPrefix(xsdNamespace) == null)
writer.WriteAttributeString("xmlns", "xsd", null, xsdNamespace);
}
Alternative approach if more changes desired to achieve nice looking XML is to allow serialization to finish and than process XML to adjust prefixes definitions (i.e. normalize to desired prefixes, collect all namespaces and define all on top). One can either read XML with C# code or even create XSLT transformation.

how to serialize class objects to xml document

I hope to find a solution from you. What I need is to serialize ValidatorList class object into an xml document. How to do this?
I tried like this:
XmlSerializer _serializer = new XmlSerializer(list);
But I don't know how to make output of xml for list that has several classes.
C#
_list= new ListVal();
Type _type = typeof(ras);
_list.Add(new RequiredField
{
Property = _type.GetProperty("CustRef")
}.Add(new AsciiVal()));
_list.Add(new RequiredField
{
Property = _type.GetProperty("ctr")
}.Add(new StringLengthVal
{
min= 3,
max= 3
}));
[Serializable]
public class Field
{
public Field Next
{
get;
set;
}
public Field TypeName
{
get;
set;
}
public Field PropertyName
{
get;
set;
}
}
public class RequiredField : Field
{
//TODO
}
public class AsciiVal: Field
{
//TODO
}
public class StringLengthVal: Field
{
//TODO
}
public class ListVal: List<Field>
{
//TODO
}
I have something close, but not exactly the Xml you want. In actual fact I think you'll see that the Xml produced below makes a bit more sense than what you have.
To get you started, you control the serialization and deserialization using attributes in the System.Xml.Serialization namespace. A few useful ones to read up on are
XmlRootAttribute
XmlElementAttribute
XmlAttributeAttribute
XmlIncludeAttribute
So I mocked up some code which closely matches your own. Notice the addition of some attributes to instruct the serializer how I want the Xml to be laid out.
[XmlInclude(typeof(AsciiValidator))]
[XmlInclude(typeof(RequiredValidator))]
[XmlInclude(typeof(StringLengthValidator))]
public class FieldValidator
{
[XmlElement("Next")]
public FieldValidator Next
{
get;
set;
}
[XmlElement("PropertyName")]
public string PropertyName
{
get;
set;
}
}
public class AsciiValidator: FieldValidator
{
}
public class RequiredValidator: FieldValidator
{
}
public class StringLengthValidator: FieldValidator
{
[XmlElement]
public int MinLength{get;set;}
[XmlElement]
public int MaxLength{get;set;}
}
[XmlRoot("ValidatorList")]
public class ValidatorList : List<FieldValidator>
{
}
Point of interest; Every class inheriting FieldValidator must be added to the list of known types using XmlIncludeAttribute so the serializer knows what to do with them)
Then I created an example object map:
var test = new ValidatorList();
test.Add(
new RequiredValidator()
{
PropertyName="CustRef",
Next = new AsciiValidator()
});
test.Add(
new RequiredValidator()
{
PropertyName="CurrencyIndicator",
Next = new StringLengthValidator(){
MinLength=3,
MaxLength = 10
}
});
Finally I told the serializer to serialize it (and output the result to the console)
var ser = new XmlSerializer(typeof(ValidatorList));
ser.Serialize(Console.Out,test);
This was the result:
<?xml version="1.0" encoding="utf-8"?>
<ValidatorList xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<FieldValidator xsi:type="RequiredValidator">
<Next xsi:type="AsciiValidator" />
<PropertyName>CustRef</PropertyName>
</FieldValidator>
<FieldValidator xsi:type="RequiredValidator">
<Next xsi:type="StringLengthValidator">
<MinLength>3</MinLength>
<MaxLength>10</MaxLength>
</Next>
<PropertyName>CurrencyIndicator</PropertyName>
</FieldValidator>
</ValidatorList>
Not a million miles away from what you wanted. There is the need to output certain things in a certain way (eg xsi:type tells the serializer how to deserialize back to the object map). I hope this gives you a good start.
Here is a live, working example: http://rextester.com/OXPOB95358
Deserialization can be done by calling the Deserialize method on the XmlSerializer.
For example, if your xml is in a string:
var ser = new XmlSerializer(typeof(ValidatorList));
var test = "<..../>" // Your Xml
var xmlReader = XmlReader.Create(new StringReader(test));
var validatorList = (ValidatorList)ser.Deserialize(xmlReader);
There are many overrides of Deserialize which take differing inputs depending if the data is in a Stream an existing reader, or saved to a file.
You have to decorate the class that contains the _validators field with the KonwnType attribute
[Serializable]
[KwownType(typeof(RequiredFieldValidator)]
[KwownType(typeof(AsciValidator)]
public class MySerialisableClass
I have several SO answers that detail how to serialize objects using XML. I'll provide links below.
However, since you're looking for a rather simple serialization of your object, you may want to read up on the DataContractSerializer. It's much less complicated than the old .NET 1.x XML Serialization.
Here's the list of SO answers:
Omitting all xsi and xsd namespaces when serializing an object in .NET?
XmlSerializer: remove unnecessary xsi and xsd namespaces
Suppress xsi:nil but still show Empty Element when Serializing in .Net
Using XmlAttributeOverrides on Nested Properties
Even though many of these have to do with XML serialization and namespaces, they contain complete examples of serializing an object to XML using .NET 1.x XML Serialization.

How to add XmlInclude attribute dynamically

I have the following classes
[XmlRoot]
public class AList
{
public List<B> ListOfBs {get; set;}
}
public class B
{
public string BaseProperty {get; set;}
}
public class C : B
{
public string SomeProperty {get; set;}
}
public class Main
{
public static void Main(string[] args)
{
var aList = new AList();
aList.ListOfBs = new List<B>();
var c = new C { BaseProperty = "Base", SomeProperty = "Some" };
aList.ListOfBs.Add(c);
var type = typeof (AList);
var serializer = new XmlSerializer(type);
TextWriter w = new StringWriter();
serializer.Serialize(w, aList);
}
}
Now when I try to run the code I got an InvalidOperationException at last line saying that
The type XmlTest.C was not expected. Use the XmlInclude or SoapInclude attribute to specify types that are not known statically.
I know that adding a [XmlInclude(typeof(C))] attribute with [XmlRoot] would solve the problem. But I want to achieve it dynamically. Because in my project class C is not known prior to loading. Class C is being loaded as a plugin, so it is not possible for me to add XmlInclude attribute there.
I tried also with
TypeDescriptor.AddAttributes(typeof(AList), new[] { new XmlIncludeAttribute(c.GetType()) });
before
var type = typeof (AList);
but no use. It is still giving the same exception.
Does any one have any idea on how to achieve it?
Two options; the simplest (but giving odd xml) is:
XmlSerializer ser = new XmlSerializer(typeof(AList),
new Type[] {typeof(B), typeof(C)});
With example output:
<AList xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<ListOfBs>
<B />
<B xsi:type="C" />
</ListOfBs>
</AList>
The more elegant is:
XmlAttributeOverrides aor = new XmlAttributeOverrides();
XmlAttributes listAttribs = new XmlAttributes();
listAttribs.XmlElements.Add(new XmlElementAttribute("b", typeof(B)));
listAttribs.XmlElements.Add(new XmlElementAttribute("c", typeof(C)));
aor.Add(typeof(AList), "ListOfBs", listAttribs);
XmlSerializer ser = new XmlSerializer(typeof(AList), aor);
With example output:
<AList xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<b />
<c />
</AList>
In either case you must cache and re-use the ser instance; otherwise you will haemorrhage memory from dynamic compilation.
Building on Marc's first answer (I only have to read, so I don't need to prevent the weird output), I use a more dynamic/generic type-array to account for unknown types, inspired by this codeproject.
public static XmlSerializer GetSerializer()
{
var lListOfBs = (from lAssembly in AppDomain.CurrentDomain.GetAssemblies()
from lType in lAssembly.GetTypes()
where typeof(B).IsAssignableFrom(lType)
select lType).ToArray();
return new XmlSerializer(typeof(AList), lListOfBs);
}
(One could probably make it more efficient, e.g. using a static or read-only type-array in stead of a local variable. That would avoid repeatedly using Reflection. But I don't know enough about when assemblies get loaded and classes and properties get initialized, to know if that would get you into trouble. My usage is not that much, to take the time to investigate this all, so I just use the same Reflection multiple times.)
Have a look at the documentation of XmlSerializer. There is a constructor which expects known types as the second parameter. That should work fine for you use case.
I'm don't think attributes can be applied at runtime, as they are used to create Meta-data at the CIL code.

Categories