I would like to manage multiple customer standards efficiently.
If I open (deserialize) an XML, I want to determine which classes are used during deserialisation. Choosing another class basically means looking at the XML from an other perspective (view).
What I have right now:
I have a class Project which has some properties and methods.
I am able to serialize instances of motor to XML, this works fine.
Also deserialization works fine.
Now I create a new class ProjectCustomerA, which is derived from the base class Project. I overwrite some of the methods on ProjectCustomerA and might add some in the future.
Both class Project and ProjectCustomerA share the same XmlType ([Serializable, XmlType("Project")]).
Now when I deserialize an XML I get an error that both classes use the same XmlType and that this is not possible.
Below is the message I get (it was originally in Dutch and I translated):
System.InvalidOperationException HResult=0x80131509 ... Inner
Exception 1: InvalidOperationException: The types C4M_Data.C4M_Project
and C4M_Data_customer.C4M_Project_Customer both use the XML-typename,
Project, from namespace . Use XML-attributes to define a unique
XML-name and/or -namespace for the type.
My question is how to read (deserialize) the same XML and let me control what classes are instantiated in my application during this process?
My current idea is that different types (all the same baseclass if needed) should result in an XML with the same root element and namespace. The XML should always look the same. Then I need to control / force the XmlSerializer to deserialize to the type I want, regardless of the root element name and namespace. Is this possible?
You cannot have multiple types in the type hierarchy have identical [XmlType] attributes. If you do, the XmlSerializer constructor will throw the exception you have seen, stating:
Use XML-attributes to define a unique XML-name and/or -namespace for the type.
The reason XmlSerializer requires unique element names and/or namespaces for all types in the hierarchy is that it is designed to be able to successfully serialize type information via the xsi:type mechanism - which becomes impossible if the XML names & namespaces are identical. You wish to make all the types in your root data model hierarchy be indistinguishable when serialized to XML which conflicts with this design intent of XmlSerializer.
Instead, when serializing, you can construct your XmlSerializer with the XmlSerializer(Type, XmlRootAttribute) constructor to specify a shared root element name and namespace to be used for all objects in your root model hierarchy. Then when deserializing you can construct an XmlSerializer using the root element name and namespace actually encountered in the file. The following extension methods do the job:
public static partial class XmlSerializationHelper
{
public static T LoadFromXmlAsType<T>(this string xmlString)
{
return new StringReader(xmlString).LoadFromXmlAsType<T>();
}
public static T LoadFromXmlAsType<T>(this TextReader textReader)
{
using (var xmlReader = XmlReader.Create(textReader, new XmlReaderSettings { CloseInput = false }))
return xmlReader.LoadFromXmlAsType<T>();
}
public static T LoadFromXmlAsType<T>(this XmlReader xmlReader)
{
while (xmlReader.NodeType != XmlNodeType.Element)
if (!xmlReader.Read())
throw new XmlException("No root element");
var serializer = XmlSerializerFactory.Create(typeof(T), xmlReader.LocalName, xmlReader.NamespaceURI);
return (T)serializer.Deserialize(xmlReader);
}
public static string SaveToXmlAsType<T>(this T obj, string localName, string namespaceURI)
{
var sb = new StringBuilder();
using (var writer = new StringWriter(sb))
obj.SaveToXmlAsType(writer, localName, namespaceURI);
return sb.ToString();
}
public static void SaveToXmlAsType<T>(this T obj, TextWriter textWriter, string localName, string namespaceURI)
{
using (var xmlWriter = XmlWriter.Create(textWriter, new XmlWriterSettings { CloseOutput = false, Indent = true }))
obj.SaveToXmlAsType(xmlWriter, localName, namespaceURI);
}
public static void SaveToXmlAsType<T>(this T obj, XmlWriter xmlWriter, string localName, string namespaceURI)
{
var serializer = XmlSerializerFactory.Create(obj.GetType(), localName, namespaceURI);
serializer.Serialize(xmlWriter, obj);
}
}
public static class XmlSerializerFactory
{
// To avoid a memory leak the serializer must be cached.
// https://stackoverflow.com/questions/23897145/memory-leak-using-streamreader-and-xmlserializer
// This factory taken from
// https://stackoverflow.com/questions/34128757/wrap-properties-with-cdata-section-xml-serialization-c-sharp/34138648#34138648
readonly static Dictionary<Tuple<Type, string, string>, XmlSerializer> cache;
readonly static object padlock;
static XmlSerializerFactory()
{
padlock = new object();
cache = new Dictionary<Tuple<Type, string, string>, XmlSerializer>();
}
public static XmlSerializer Create(Type serializedType, string rootName, string rootNamespace)
{
if (serializedType == null)
throw new ArgumentNullException();
if (rootName == null && rootNamespace == null)
return new XmlSerializer(serializedType);
lock (padlock)
{
XmlSerializer serializer;
var key = Tuple.Create(serializedType, rootName, rootNamespace);
if (!cache.TryGetValue(key, out serializer))
cache[key] = serializer = new XmlSerializer(serializedType, new XmlRootAttribute { ElementName = rootName, Namespace = rootNamespace });
return serializer;
}
}
}
Then, if your type hierarchy looks something like this:
public class Project
{
// Name for your root element. Replace as desired.
public const string RootElementName = "Project";
// Namespace for your project. Replace as required.
public const string RootElementNamespaceURI = "https://stackoverflow.com/questions/49977144";
public string BaseProperty { get; set; }
}
public class ProjectCustomerA : Project
{
public string CustomerProperty { get; set; }
public string ProjectCustomerAProperty { get; set; }
}
public class ProjectCustomerB : Project
{
public string CustomerProperty { get; set; }
public string ProjectCustomerBProperty { get; set; }
}
You can serialize an instance of ProjectCustomerA and deserialize it as an instance of ProjectCustomerB as follows:
var roota = new ProjectCustomerA
{
BaseProperty = "base property value",
CustomerProperty = "shared property value",
ProjectCustomerAProperty = "project A value",
};
var xmla = roota.SaveToXmlAsType(Project.RootElementName, Project.RootElementNamespaceURI);
var rootb = xmla.LoadFromXmlAsType<ProjectCustomerB>();
var xmlb = rootb.SaveToXmlAsType(Project.RootElementName, Project.RootElementNamespaceURI);
// Assert that the shared BaseProperty was deserialized successfully.
Assert.IsTrue(roota.BaseProperty == rootb.BaseProperty);
// Assert that the same-named CustomerProperty was ported over properly.
Assert.IsTrue(roota.CustomerProperty == rootb.CustomerProperty);
Notes:
I chose to put the shared XML element name and namespace as constants in the base type Project.
When constructing an XmlSerializer with an override root element name or namespace, it must be cached to avoid a memory leak.
All this being said, making it be impossible to determine whether a given XML file contains an object of type ProjectCustomerA or ProjectCustomerB seems like a dangerously inflexible design going forward. I'd encourage you to rethink whether this design is appropriate. For instance, you could instead serialize them with their default, unique element names and namespaces, and still deserialize to any desired type using the methods LoadFromXmlAsType<T>() above, which generate an XmlSerializer using the actual name and namespace found in the file.
The methods LoadFromXmlAsType<T>() may not work if there is an xsi:type attribute on the root element. If you want to ignore (or process) the xsi:type attribute then further work may be required.
Sample working .Net fiddle.
Related
I am successfully using XML serializer to serialize a Microsoft.VisualStudio.Services.Common.SerializableDictionary<T1, T2>.
Now I want to add a guid string to SerializableDictionary and serialize it as well. I therefore created the following class:
public class SerializableDictWithGuid<T1, T2> : SerializableDictionary<T1, T2>, IMyInterface
{
[XmlElement(ElementName = idXml001)]
public string Guid { get; set; } = string.Empty;
}
When serializing SerializableDictWithGuid with the below code, the guid string is missing in the XML output.
public class XmlSerializeHelper<T> where T : class
{
public static string Serialize(T obj)
{
try
{
System.Xml.Serialization.XmlSerializer xsSubmit = new System.Xml.Serialization.XmlSerializer(typeof(T));
using (var sww = new StringWriter())
{
using (XmlTextWriter writer = new XmlTextWriter(sww) { Formatting = Formatting.Indented })
{
xsSubmit.Serialize(writer, obj);
}
return sww.ToString();
}
}
catch (Exception ex)
{
logger.log(ex);
}
return string.Empty;
}
}
Minimal reproducible example
SerializableDictWithGuid <string, string> test = new SerializableDictWithGuid <string, string>() { Guid = "123" };
XmlSerializeHelper<SerializableDictWithGuid <string, string>>.Serialize(test);
Question: how can I add properties to SerializableDictionary and include them in XML serialization?
XmlSerializer does not support dictionaries, so SerializableDictionary<T1, T2> works by implementing IXmlSerializable. I.e., it serializes itself manually using handcrafted code. As such, that code knows nothing about the properties of subclasses, and doesn't have any code to discover and serialize them automatically.
Luckily it appears that IXmlSerializable.ReadXml() and IXmlSerializable.WriteXml() were not implemented explicitly, so it should be possible to re-implement them via interface re-implementation and still call the base class methods. And, while in general it's tricky to implement IXmlSerializable, if you make your Guid be an XML attribute rather than an element, implementation becomes simple because, while the design of IXmlSerializable doesn't allow for derived classes to inject child elements, it does allow for derived classes to inject custom attributes:
public class SerializableDictWithGuid<T1, T2> : SerializableDictionary<T1, T2>, IMyInterface, IXmlSerializable // Re-implement IXmlSerializable
{
const string idXml001 = "idXml001";
public string Guid { get; set; } = string.Empty;
public new void ReadXml(XmlReader reader)
{
reader.MoveToContent();
// At this point the reader is positioned on the beginning of the container element for the dictionary, so we can get any custom attributes we wrote.
Guid = reader.GetAttribute(idXml001);
base.ReadXml(reader);
}
public new void WriteXml(XmlWriter writer)
{
// At this point the container element has been written but nothing else, so it's still possible to add some attributes.
if (Guid != null)
writer.WriteAttributeString(idXml001, Guid);
base.WriteXml(writer);
}
}
This results in XML that looks like:
<SerializableDictWithGuidOfStringString idXml001="123">
<item>
<key>
<string>hello</string>
</key>
<value>
<string>there</string>
</value>
</item>
</SerializableDictWithGuidOfStringString>
The XML Standard states that
Attributes are used to associate name-value pairs with elements.
In general attributes are appropriate for fixed sets of simple, primitive values such as names and IDs. Complex values with multiple internal properties, or values that repeat (i.e. collections) should be child elements, not attributes. Since an identifying Guid is a primitive value, using an attribute is appropriate. See:
XML attribute vs XML element
Should I use Elements or Attributes in XML?
Demo fiddle here.
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 am attempting to serialize just part of a class. I've added XML attributes to the class members so that the generated XML tags are correctly named to match a spec regardless of what my properties are named. This works fine when serializing the main class. However, if I just want to serialize part of the class, I lose the XML attributes and the names go back to their defaults. Is there a way to retain the XML attributes when serializing just part of a class?
[XmlRoot ("someConfiguration")]
public class SomeConfiguration
{
[XmlArray("bugs")]
[XmlArrayItem("bug")]
public List<string> Bugs { get; set; }
}
When I serialize the entire class, I get this (which is exactly as I would expect):
<someConfiguration>
<bugs>
<bug>Bug1</bug>
<bug>Bug2</bug>
<bug>Bug3</bug>
</bugs>
</someConfiguration>
If I attempt to just serialize the 'Bugs' part of the class, I get this (note the XML attributes that change the tag names are all ignored):
<ArrayOfString>
<string>Bug1</string>
<string>Bug2</string>
<string>Bug3</string>
</ArrayOfString>
I need to get this:
<bugs>
<bug>Bug1</bug>
<bug>Bug2</bug>
<bug>Bug3</bug>
</bugs>
How do I get the partial class to serialize with the above tags?
Or better yet, is there a way to specify tag names when serializing a simple List<object>. So that you can specify the tag used for the list instead of it using <ArrayOfobject> and specify the tag used for the array items instead of <object>?
is there a way to specify tag names when serializing a simple List.
In general, depending on the exact scenario, it may be possible to get this to work. See MSDN's How to: Specify an Alternate Element Name for an XML Stream. The example there involves overriding serialization of a specific field, but it may be possible to use the same technique to override whole type names as well.
But it seems like an awful lot of trouble to me. Instead, why not just handle the serialization explicitly:
private static string SerializeByLinqAndToString<T>(
List<T> data, string rootName, string elementName)
{
XDocument document = new XDocument(
new XElement(rootName, data.Select(s => new XElement(elementName, s))));
return SaveXmlToString(document);
}
private static string SaveXmlToString(XDocument document)
{
StringBuilder sb = new StringBuilder();
using (XmlWriter xmlWriter = XmlWriter.Create(sb,
new XmlWriterSettings { Indent = true, OmitXmlDeclaration = true }))
{
document.Save(xmlWriter);
}
return sb.ToString();
}
Call like this:
SomeConfiguration config = ...; // initialize as desired
string result = SerializeByLinq(config.Bugs, "bug", "bugs");
The above works only with a list of strings, or a list of types where the element content can be simply the result of calling ToString() on the instance of the type.
Using the full-blown serialization features in .NET might be worthwhile when dealing with complex types, but if all you've got is a simple list of strings, the LINQ-to-XML feature is very handy.
If you do have more complex types, you can transform each list element into an XElement for the DOM and serialize that:
private static string SerializeByLinq<T>(
List<T> data, string rootName, string elementName = null)
{
XDocument document = new XDocument(
new XElement(rootName, data.Select(t =>
ElementFromText(SerializeObject(t), elementName)
)));
return SaveXmlToString(document);
}
private static XElement ElementFromText(string xml, string name = null)
{
StringReader reader = new StringReader(xml);
XElement result = XElement.Load(reader);
if (!string.IsNullOrEmpty(name))
{
result.Name = name;
}
return result;
}
private static string SerializeObject<T>(T o)
{
XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
StringWriter textWriter = new StringWriter();
using (XmlWriter writer = XmlWriter.Create(textWriter,
new XmlWriterSettings { Indent = true, OmitXmlDeclaration = true }))
{
xmlSerializer.Serialize(writer, o,
new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty}));
}
return textWriter.ToString();
}
In this second example, you can omit the element name for the child, and it will just use whatever the type's set up to use already (e.g. the type name, or whatever [XmlRoot] is set to).
Just throwing this out there, you could wrap the List<> inside a custom class:
[XmlRoot("config")]
public class SomeConfiguration
{
[XmlElement("bugs")]
public BugList Bugs { get; set; }
[XmlElement("trees")]
public TreeList Trees { get; set; }
}
[XmlRoot("bugs")]
public class BugList
{
[XmlElement("bug")]
public List<string> Items = new List<string>();
}
[XmlRoot("trees")]
public class TreeList
{
[XmlElement("tree")]
public List<string> Items = new List<string>();
}
That will now allow you to serialize the individual Lists and they'll be rooted as you'd expect.
void Main()
{
var config = new SomeConfiguration
{
Bugs = new BugList { Items = { "Bug1", "Bug2" } },
Trees = new TreeList { Items = { "Tree1", "Tree2" } }
};
// Your config will work as normal.
Debug.WriteLine(ToXml(config)); // <config> <bugs>.. <trees>..</config>
// Your collections are now root-ed properly.
Debug.WriteLine(ToXml(config.Bugs)); // <bugs><bug>Bug1</bug><bug>Bug2</bug></bugs>
Debug.WriteLine(ToXml(config.Trees)); // <trees><tree>Tree1</tree><tree>Tree2</tree></trees>
}
public string ToXml<T>(T obj)
{
var ser = new XmlSerializer(typeof(T));
var emptyNs = new XmlSerializerNamespaces();
emptyNs.Add("","");
using (var stream = new MemoryStream())
{
ser.Serialize(stream, obj, emptyNs);
return Encoding.ASCII.GetString(stream.ToArray());
}
}
Found a 'work-around' way to do it.. Instead of putting XMLArray and XMLArrayList attributes above the List<>:
[XmlRoot ("someConfiguration")]
public class SomeConfiguration
{
[XmlArray("bugs")]
[XmlArrayItem("bug")]
public List<string> Bugs { get; set; }
}
Put an XmlElement attribute on the list which will specify the tag to be used for each element and not have a tag wrapping the list. Your class tag will in effect do that for you.
[XmlRoot ("bugs")]
public class SomeConfiguration
{
[XmlElement("bug")]
public List<string> Bugs { get; set; }
}
When you serialize the above, you will end up with:
<bugs>
<bug>Bug1</bug>
<bug>Bug2</bug>
<bug>Bug3</bug>
</bugs>
I've got a class which has been serialized into JSON, and which I'm trying to deserialize into an object.
e.g.
public class ContentItemViewModel
{
public string CssClass { get; set; }
public MyCustomClass PropertyB { get; set; }
}
the simple property (CssClass) will deserialize with:
var contentItemViewModels = ser.Deserialize<ContentItemViewModel>(contentItems);
But PropertyB gets an error...
We added a JavaScriptConverter:
ser.RegisterConverters(new List<JavaScriptConverter>{ publishedStatusResolver});
But when we added 'MyCustomClass' as a 'SupportedType', the Deserialize method was never called. However when we have ContentItemViewModel as the SupportedType, then Deserialize is called.
We've got a current solution which looks something like this:
class ContentItemViewModelConverter : JavaScriptConverter
{
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
var cssClass = GetString(dictionary, "cssClass"); //I'm ommitting the GetString method in this example...
var propertyB= GetString(dictionary, "propertyB");
return new ContentItemViewModel{ CssClass = cssClass ,
PropertyB = new MyCustomClass(propertyB)}
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
throw new Exception("Only does the Deserialize");
}
public override IEnumerable<Type> SupportedTypes
{
get
{
return new List<Type>
{
typeof(ContentItemViewModel)
};
}
}
}
But we'd prefer a simpler solution of only deserializing MyCustomClass, as there are a number of other fields which are on the ViewModel, and it seems a waste to have to edit this converter every time we change/add a property....
Is there a way to Deserialize JUST PropertyB of type MyCustomClass?
Thanks for your help!
Have you considered using DatacontractJsonSerializer
[DataContract]
public class MyCustomClass
{
[DataMember]
public string foobar { get; set; }
}
[DataContract]
public class ContentItemViewModel
{
[DataMember]
public string CssClass { get; set; }
[DataMember]
public MyCustomClass PropertyB { get; set; }
}
class Program
{
static void Main(string[] args)
{
ContentItemViewModel model = new ContentItemViewModel();
model.CssClass = "StackOver";
model.PropertyB = new MyCustomClass();
model.PropertyB.foobar = "Flow";
//Create a stream to serialize the object to.
MemoryStream ms = new MemoryStream();
// Serializer the User object to the stream.
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(ContentItemViewModel));
ser.WriteObject(ms, model);
byte[] json = ms.ToArray();
ms.Close();
string s= Encoding.UTF8.GetString(json, 0, json.Length);
Console.ReadLine();
}
}
Add all possible classes to DatacontractJsonSerializer.KnownTypes if MyCustomClass has derivations.
For whatever it may be worth after all this time, but I stumbled over the same problem and the solution is that the Deserializer hasn't got a clue about the classes you are deserializing unless you give him the necessary information.
On the top level, it knows the type from the type parameter of Deserialize<>(). That's why your converter for ContentItemViewModel works. For nested objects, it needs __type properties and a JavaScriptTypeResolver.
var ser = new JavaScriptSerializer(new SimpleTypeResolver());
ser.RegisterConverters(myconverters);
MyClass myObject = new MyClass();
string json = ser.Serialize(myObject);
// set a breakpoint here to see what has happened
ser.Deserialize<MyClass>(json);
A TypeResolver adds a __type property to each serialized object. You can write a custom type resolver that uses short names. In this sample, I use the SimpleTypeResolver from .net that "simply" stores the fully qualified type name as __type. When deserializing, the JavaScriptDeserializer finds __type and asks the TypeResolver for the correct type. Then it knows a type and can call a registered JavaScriptConverter.Deserialize method.
Without a TypeResolver, objects are deserialized to a Dictionary because JavaScriptSerializer doesn't have any type information.
If you can't provide a __type property in your json string, I think you'll need to deserialize to Dictionary first and then add a "guessing-step" that interprets the fields to find the right type. Then, you can use the ConvertToType method of JavaScriptSerializer to copy the dictionary into the object's fields and properties.
If you need to use the JavaScriptSerializer that is provides by ASP.NET and can't create your own, consider this section from the .ctor help of JavaScriptSerializer:
The instance of JavaScriptSerializer that is used by the asynchronous communication layer for invoking Web services from client script uses a special type resolver. This type resolver restricts the types that can be deserialized to those defined in the Web service’s method signature, or the ones that have the GenerateScriptTypeAttribute applied. You cannot modify this built-in type resolver programmatically.
Perhaps the GenerateScriptType Attribute can help you. But I don't know what kind of __type Properties are be needed here.
A base project contains an abstract base class Foo. In separate client projects, there are classes implementing that base class.
I'd like to serialize and restore an instance of a concrete class by calling some method on the base class:
// In the base project:
public abstract class Foo
{
abstract void Save (string path);
abstract Foo Load (string path);
}
It can be assumed that at the time of deserialization, all needed classes are present. If possible in any way, the serialization should be done in XML. Making the base class implement IXmlSerializable is possible.
I'm a bit stuck here. If my understanding of things is correct, then this is only possible by adding an [XmlInclude(typeof(UnknownClass))] to the base class for every implementing class - but the implementing classes are unknown!
Is there a way to do this? I've got no experience with reflection, but i also welcome answers using it.
Edit: The problem is Deserializing. Just serializing would be kind of easy. :-)
You can also do this at the point of creating an XmlSerializer, by providing the additional details in the constructor. Note that it doesn't re-use such models, so you'd want to configure the XmlSerializer once (at app startup, from configuration), and re-use it repeatedly... note many more customizations are possible with the XmlAttributeOverrides overload...
using System;
using System.Collections.Generic;
using System.IO;
using System.Xml.Serialization;
static class Program
{
static readonly XmlSerializer ser;
static Program()
{
List<Type> extraTypes = new List<Type>();
// TODO: read config, or use reflection to
// look at all assemblies
extraTypes.Add(typeof(Bar));
ser = new XmlSerializer(typeof(Foo), extraTypes.ToArray());
}
static void Main()
{
Foo foo = new Bar();
MemoryStream ms = new MemoryStream();
ser.Serialize(ms, foo);
ms.Position = 0;
Foo clone = (Foo)ser.Deserialize(ms);
Console.WriteLine(clone.GetType());
}
}
public abstract class Foo { }
public class Bar : Foo {}
You don't have to put the serialization functions into any base class, instead, you can add it to your Utility Class.
e.g. ( the code is for example only, rootName is optional )
public static class Utility
{
public static void ToXml<T>(T src, string rootName, string fileName) where T : class, new()
{
XmlSerializer serializer = new XmlSerializer(typeof(T), new XmlRootAttribute(rootName));
XmlTextWriter writer = new XmlTextWriter(fileName, Encoding.UTF8);
serializer.Serialize(writer, src);
writer.Flush();
writer.Close();
}
}
Simply make call to
Utility.ToXml( fooObj, "Foo", #"c:\foo.xml");
Not only Foo's family types can use it, but all other serializable objects.
EDIT
OK full service... (rootName is optional)
public static T FromXml<T>(T src, string rootName, string fileName) where T : class, new()
{
XmlSerializer serializer = new XmlSerializer(typeof(T), new XmlRootAttribute(rootName));
TextReader reader = new StreamReader(fileName);
return serializer.Deserialize(reader) as T;
}
Well the serialization shouldn't be a problem, the XmlSerializer constructor takes a Type argument, even calling GetType on an instance of a derived class through a method on the abstract base will return the derived types actual Type. So in essence as long as you know the proper type upon deserialization then the serialization of the proper type is trivial. So you can implement a method on the base called serialize or what have you that passes this.GetType() to the constructor of the XmlSerializer.. or just passes the current reference out and lets the serialize method take care of it and you should be fine.
Edit: Update for OP Edit..
If you don't know the type at deserialization then you really have nothing but a string or byte array, without some sort of identifier somewhere you are kind of up a creek. There are some things you can do like trying to deserialize as every known derived type of the xx base class, I would not recommend this.
Your other option is to walk the XML manually and reconstruct an object by embedding the type as a property or what have you, maybe that is what you originally meant in the article, but as it stands I don't think there is a way for the built in serialization to take care of this for you without you specifying the type.
Somewhere deep inside the XML namespaces lies a wonderful class called XmlReflectionImporter.
This may be of help to you if you need to create a schema at runtime.
You can also do this by creating an XmlSerializer passign in all possible types to the constructor. Be warned that when you use this constructor the xmlSerializer will be compiled each and every time and will result in a leak if you constantly recreate it. You will want to create a single serializer and reuse it in your application.
You can then bootstrap the serializer and using reflection look for any descendants of foo.
These links will probably be helpful to you:
CodeProject article
Blog post
Stack Overflow question
I have a complex remoting project and wanted very tight control over the serialized XML. The server could receive objects that it had no idea how to deserialize and vice versa, so I needed a way to identify them quickly.
All of the .NET solutions I tried lacked the needed flexibility for my project.
I store an int attribute in the base xml to identify the type of object.
If I need to create a new object from xml I created a factory class that checks the type attribute then creates the appropriate derived class and feeds it the xml.
I did something like this (pulling this out of memory, so syntax may be a little off):
(1) Created an interface
interface ISerialize
{
string ToXml();
void FromXml(string xml);
};
(2) Base class
public class Base : ISerialize
{
public enum Type
{
Base,
Derived
};
public Type m_type;
public Base()
{
m_type = Type.Base;
}
public virtual string ToXml()
{
string xml;
// Serialize class Base to XML
return string;
}
public virtual void FromXml(string xml)
{
// Update object Base from xml
}
};
(3) Derived class
public class Derived : Base, ISerialize
{
public Derived()
{
m_type = Type.Derived;
}
public override virtual string ToXml()
{
string xml;
// Serialize class Base to XML
xml = base.ToXml();
// Now serialize Derived to XML
return string;
}
public override virtual void FromXml(string xml)
{
// Update object Base from xml
base.FromXml(xml);
// Update Derived from xml
}
};
(4) Object factory
public ObjectFactory
{
public static Base Create(string xml)
{
Base o = null;
Base.Type t;
// Extract Base.Type from xml
switch(t)
{
case Base.Type.Derived:
o = new Derived();
o.FromXml(xml);
break;
}
return o;
}
};
This method reads the XML root element and checks if the current executing assembly contains a type with such a name. If so, the XML document is deserialized. If not, an error is thrown.
public static T FromXml<T>(string xmlString)
{
Type sourceType;
using (var stringReader = new StringReader(xmlString))
{
var rootNodeName = XElement.Load(stringReader).Name.LocalName;
sourceType =
Assembly.GetExecutingAssembly().GetTypes()
.FirstOrDefault(t => t.IsSubclassOf(typeof(T))
&& t.Name == rootNodeName)
??
Assembly.GetAssembly(typeof(T)).GetTypes()
.FirstOrDefault(t => t.IsSubclassOf(typeof(T))
&& t.Name == rootNodeName);
if (sourceType == null)
{
throw new Exception();
}
}
using (var stringReader = new StringReader(xmlString))
{
if (sourceType.IsSubclassOf(typeof(T)) || sourceType == typeof(T))
{
var ser = new XmlSerializer(sourceType);
using (var xmlReader = new XmlTextReader(stringReader))
{
T obj;
obj = (T)ser.Deserialize(xmlReader);
xmlReader.Close();
return obj;
}
}
else
{
throw new InvalidCastException(sourceType.FullName
+ " cannot be cast to "
+ typeof(T).FullName);
}
}
}
Marking the classes as Serializable and using SoapBinaryFormatter instead of XmlSerializer will give you this functionality automatically. When serializing the type information of the instance being serialized will be written to the XML, and SoapBinaryFormatter can instantiate the subclasses when deserializing.
I used the XmlType attribute of the unknown (but expected) classes to determine the Type for the deserialization. The to be expected types are load during the instantiation of the AbstractXmlSerializer class and placed in a dictionary. During the deserialization the root element is read and with this the type is retrieved form the dictionary. After that it can be deserialized normally.
XmlMessage.class:
public abstract class XmlMessage
{
}
IdleMessage.class:
[XmlType("idle")]
public class IdleMessage : XmlMessage
{
[XmlElement(ElementName = "id", IsNullable = true)]
public string MessageId
{
get;
set;
}
}
AbstractXmlSerializer.class:
public class AbstractXmlSerializer<AbstractType> where AbstractType : class
{
private Dictionary<String, Type> typeMap;
public AbstractXmlSerializer(List<Type> types)
{
typeMap = new Dictionary<string, Type>();
foreach (Type type in types)
{
if (type.IsSubclassOf(typeof(AbstractType))) {
object[] attributes = type.GetCustomAttributes(typeof(XmlTypeAttribute), false);
if (attributes != null && attributes.Count() > 0)
{
XmlTypeAttribute attribute = attributes[0] as XmlTypeAttribute;
typeMap[attribute.TypeName] = type;
}
}
}
}
public AbstractType Deserialize(String xmlData)
{
if (string.IsNullOrEmpty(xmlData))
{
throw new ArgumentException("xmlData parameter must contain xml");
}
// Read the Data, Deserializing based on the (now known) concrete type.
using (StringReader stringReader = new StringReader(xmlData))
{
using (XmlReader xmlReader = XmlReader.Create(stringReader))
{
String targetType = GetRootElementName(xmlReader);
if (targetType == null)
{
throw new InvalidOperationException("XML root element was not found");
}
AbstractType result = (AbstractType)new
XmlSerializer(typeMap[targetType]).Deserialize(xmlReader);
return result;
}
}
}
private static string GetRootElementName(XmlReader xmlReader)
{
if (xmlReader.IsStartElement())
{
return xmlReader.Name;
}
return null;
}
}
UnitTest:
[TestMethod]
public void TestMethod1()
{
List<Type> extraTypes = new List<Type>();
extraTypes.Add(typeof(IdleMessage));
AbstractXmlSerializer<XmlMessage> ser = new AbstractXmlSerializer<XmlMessage>(extraTypes);
String xmlMsg = "<idle></idle>";
MutcMessage result = ser.Deserialize(xmlMsg);
Assert.IsTrue(result is IdleMessage);
}