XML serialize base class without knowing derived class - c#

I know that an XmlSerializer will throw an exception if the passed in type does not match the type from when the serializer was created.
My question is: Is it possible to serialize a base type without knowing the derived type? Consider this scenario:
The following classes are in a top-level domain and have few or no dependencies:
public class Serializer
{
private static string Serialize(Base baseData)
{
var serializer = new XmlSerializer(typeof(Base));
var sb = new StringBuilder();
using (var writer = XmlWriter.Create(sb))
{
serializer.Serialize(writer, baseData);
}
return sb.ToString();
}
private static string Deserialize(...) { ... }
}
public class Base
{
public string Name { get; set; }
}
The following derived class has additional dependencies on things like UI components, composition, etc. (more on this later):
[Export(typeof(IMyComponent))]
public class Derived : Base, IMyComponent, INotifyPropertyChanged
{
public new string Name
{
get { return base.Name; }
set
{
base.Name = value;
NotifyPropertyChanged("Name");
}
}
}
If I invoke the serializer like this:
Base myData = new Derived();
myData.Name = "Foo bar";
Serializer.Serialize(myData);
It throws an exception because I passed in a Derived, even though the parameters and serializer are explicitly set to Base. All I want is to serialize Base, I don't care about whatever is in Derived.
My goal here is to create the derived class with a UI, but only serialize the base class. The reason is that I am deserializing the XML in a separate assembly that must not contain additional dependencies beyond the .NET framework. The serializer and base objects are recreated/copied into this isolated assembly so that the namespaces and types will match, so that when deserialization occurs, the types will be deserialized correctly. But I can't include a copy of Derived because it has dependencies on UI, MEF, other third party libraries, etc.
So, how can I force the serializer to serialize the base class even if I pass in a derived class? Again, I don't care if I lose the information in Derived, all I am concerned about is Base.

Since you are looking to only serialize the Base properties, you have to create an instance of Base and map the properties from the derived type. Serializers are programmed to be smart, sometimes too smart in your case.
Look into AutoMapper or just do it manually.
public class Serializer
{
private static string Serialize(Base baseData)
{
var mappedBase = new Base();
// Do mapping
mappedBase.Foo = baseData.Foo;
var serializer = new XmlSerializer(typeof(Base));
var sb = new StringBuilder();
using (var writer = XmlWriter.Create(sb))
{
serializer.Serialize(writer, mappedBase);
}
return sb.ToString();
}
private static string Deserialize(...) { ... }
}

One possible solution would be:
public class Serializer
{
public static string Serialize(Base baseData)
{
var baseType = baseData.GetType().BaseType;
var baseTypeFieldInfo = baseType.GetProperties();
var newBaseInstance = new Base();
var newBaseInstanceType = newBaseInstance.GetType();
//ONLY gets public properties but could be applied to fields
var newBaseTypeFieldInfo = newBaseInstanceType.GetProperties();
foreach (var bt in baseTypeFieldInfo)
{
foreach (var nbt in newBaseTypeFieldInfo)
{
if (nbt.Name == bt.Name)
{
nbt.SetValue(newBaseInstance, bt.GetValue(baseData, null), null);
}
}
}
var serializer = new XmlSerializer(typeof(Base));
var sb = new StringBuilder();
using (var writer = XmlWriter.Create(sb))
{
serializer.Serialize(writer, newBaseInstance);
}
return sb.ToString();
}
}
public class Base
{
public string Name { get; set; }
}
public class Derived : Base
{
public string AnotherName { get; set; }
public new string Name
{
get { return base.Name; }
set { base.Name = value; }
}
}

Related

C# Json Interface serialization parent-child

this might have a easy/quick solution, but from my investigation I didn't manage to find it.
public interface IBaseMessage
{
}
public interface IRefreshMessage : IBaseMessage
{
}
Both are implemented by concrete classes in a Library.Message
They are stored inside a List<IBaseMessage> and when I serialize the object to be send over the network (one by one) it is marked as IBaseMessage even tough some are IRefrehMessage
Issue: on deserialization (on another PC) they are reconstructed as IBaseMessage and all the information from IRefreshMessage is lost.
Assumption: I believe there must be some JSON class attribute that allows me to specify into what to be serialized?
Thank you, and sorry for a rather silly question
Edit:
Using this class to serialize/deserialize:
using Newtonsoft.Json.Linq;
public class JMessage
{
public Type Type { get; set; }
public JToken Value { get; set; }
public static JMessage FromValue<T>(T value)
{
return new JMessage { Type = typeof(T), Value = JToken.FromObject(value) };
}
public static string Serialize(JMessage message)
{
return JToken.FromObject(message).ToString();
}
public static JMessage Deserialize(string data)
{
return JToken.Parse(data).ToObject<JMessage>();
}
Once is calls
string data = JMessage.Serialize(JMessage.FromValue(message));
I get:
{
"Type": "Universal_Tool_Manager.Messages.Message.Interfaces.IBaseMessage, Universal Tool Manager.Messages, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
"Value": {
"Propagation": 2,
"State": 0
}
}
Hope this helps.
JSON.NET allows you to specify how to handle serialization and deserialization using JsonSerializerSettings:
class Program
{
static void Main(string[] args)
{
var items = new List<Test>
{
new Test() {Hello = "Hi"},
new TestDerived() {Hello = "hello", Second = "World"}
};
var settings = new JsonSerializerSettings()
{
TypeNameHandling = TypeNameHandling.Objects
};
var text = JsonConvert.SerializeObject(items, settings);
var data = JsonConvert.DeserializeObject<IEnumerable<Test>>(text, settings);
}
}
public class Test
{
public string Hello { get; set; }
}
public class TestDerived : Test
{
public string Second { get; set; }
}
In this sample the generated JSON will contain a name of the actual type which was serialized and JSON.NET will use this information to deserialize back to the correct derived type - data[1] is TestDerived in my example code.
To use JsonSerializerSettings with FromObject and Parse methods, you will need to create an instance of JsonSerializer:
var serializer = sonSerializer.Create(settings);
JToken.FromObject(o, serializer)
//and
JToken.Parse(data).ToObject<JMessage>(serializer);
I've asked the wrong question here :)
public static JMessage FromValue<T>(T value)
{
return new JMessage { Type = **typeof(T)**, Value = JToken.FromObject(value) };
}
The issue was on typeof(T) which of course it will always return the base interface IBaseInterface since i'm using a
List<IBaseInterface>,
The solution was to use value.GetType(),
sorry for the rookie mistake

XMLserialize object of list and string

So i need to xml serialize a generic list of objects where the object consists of another list and a string.
This is what i get now
<?xml version="1.0" encoding="UTF-8"?>
-<ArrayOfRecept xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
-<Recept>
<Namn>Nomnom</Namn>
</Recept>
-<Recept>
<Namn>Ännu mer nomnom</Namn>
</Recept>
</ArrayOfRecept>
Here only the string value is serialized and not my List
This is my object that i want to serialize
public class Recept
{
private ListHanterare<string> ingredienser;
private string namn;
public Recept()
{
ingredienser = new ListHanterare<string>();
}
public ListHanterare<string> Ingredienser
{
get { return ingredienser; }
}
public string Namn
{
get { return namn; }
set { namn = value; }
}
}
So I will have a list of Recept that i want to xml serialize and I want the xml to show both "Namn" and the "Ingredienser"-list.
This is my serializer
class XMLSerial
{
public static bool Serialize<T>(T obj, string filePath)
{
bool bok = true;
XmlSerializer serializer = new XmlSerializer(typeof(T));
TextWriter writer = new StreamWriter(filePath);
try
{
serializer.Serialize(writer, obj);
}
catch
{
bok = false;
}
finally
{
if (writer != null)
writer.Close();
}
return bok;
}
}
This is inside the ListHanterare class where I pass the object to the serializer
public bool XMLSerialize(string filePath){
return XMLSerial.Serialize<List<T>>(lista, filePath);
EDIT:
So by adding a setter to ingredienser I now get this
-<Recept>
<Ingredienser/>
<Namn>rec</Namn>
</Recept>
But ingredienser is still empty
My ListHanterare class is a basic generic List class
public class ListHanterare<T> : IListHanterare<T>
{
private List<T> lista; // This is the list
private int count;
public ListHanterare()
{
lista = new List<T>();
}
So i need to serialize a ListHanterare list of Recept objects where the Recept object consists of a string and another ListHanterare list of strings, the string is serialized correctly but not the list of strings.
In order for XmlSerializer to serialize or deserialize the property Ingredienser of your Recept class, it must have a public setter as well as a public getter:
public ListHanterare<string> Ingredienser
{
get { return ingredienser; }
set { ingredienser = value; }
}
If you don't do this, XmlSerializer will ignore the property.
You might still have problems with the class ListHanterare, but it's not shown in your question.
DataContractSerializer does not have this requirement. However, to serialize and deserialize your classes with DataContractSerializer and include private fields or properties, you would need to fully annotate them with data contract attributes. Private field and properties marked with [DataMember] get serialized despite being private. Data contract serialization is opt-in, however, so to use this feature your classes would need to be fully attributed.
Update
You don't show the complete code of your ListHanterare<string> class. In order for XmlSerializer to serialize and deserialize it successfully, it must either:
Make all its contents available in publicly settable and gettable properties, or
Implement ICollection<T> (or even IList<T>) as described here: XmlSerializer Class.
For method #1, you could modify your class as follows:
public class ListHanterare<T>
{
private List<T> lista; // This is the list
private int count;
public ListHanterare()
{
lista = new List<T>();
}
[XmlElement("Item")]
public T[] SerializableList
{
get
{
return (lista == null ? new T [0] : lista.ToArray());
}
set
{
if (lista == null)
lista = new List<T>();
lista.Clear();
lista.AddRange(value ?? Enumerable.Empty<T>());
count = lista.Count;
}
}
}
You will now see the items serialized your XML.

Serialize Singleton via DataContract

I've problems to serialize and deserialize a singleton via DataContract.
First some facts:
1.) Singleton is "internal"
2.) Singleton contains Dictionaries
My serialization and deserialization works fine, but it isn't the right way for a singleton. If I deserialize the xml, I always generate a new instance of my singleton and overwrite the current reference of the singleton object - but after this it isn't a singleton further.
Has anybody any idea? - Thanks.
Check this link from msdn, there is an example on serializing a singleton. After deserialization you should return the reference and not the object.
Try using NetDataContractSerializer-
The NetDataContractSerializer differs from the DataContractSerializer
in one important way: the NetDataContractSerializer includes CLR type
information in the serialized XML, whereas the DataContractSerializer
does not. Therefore, the NetDataContractSerializer can be used only if
both the serializing and deserializing ends share the same CLR types.
The serializer can serialize types to which either the
DataContractAttribute or SerializableAttribute attribute has been
applied. It also serializes types that implement ISerializable.
Code example:
[DataContract(Name = "Customer", Namespace = "http://www.contoso.com")]
class Person : IExtensibleDataObject
{
[DataMember()]
public string FirstName;
[DataMember]
public string LastName;
[DataMember()]
public int ID;
public Person(string newfName, string newLName, int newID)
{
FirstName = newfName;
LastName = newLName;
ID = newID;
}
private ExtensionDataObject extensionData_Value;
public ExtensionDataObject ExtensionData
{
get { return extensionData_Value; }
set { extensionData_Value = value; }
}
}
Searialization:
Person p1 = new Person("Zighetti", "Barbara", 101);
FileStream fs = new FileStream(fileName, FileMode.Create);
XmlDictionaryWriter writer = XmlDictionaryWriter.CreateTextWriter(fs);
NetDataContractSerializer ser = new NetDataContractSerializer();
ser.WriteObject(writer, p1);
Msdn Link here
(code for .net 4.0)
I had the same problem:
the de-serialization needs to create a new instance of the singleton class, which it can(!) do because its inside a member function: the constructor is visible to members, but that instance cannot replace the singleton instance visible from the outside (the "this").
So you have to copy the properties from your de-serialized instance into the "this" instance.
Doing the copying by hand gets old fast, so here is my solution using reflection to copy the public writable members that are not marked [xmlignore]:
public static class SerializationHelpers
{
/// <summary>
/// Copy all public props and fields that are not xmlignore
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="target"></param>
/// <param name="other"></param>
public static void CopyTypeFields<T>(T target, T other)
{
// get all public static properties of MyClass type
PropertyInfo[] propertyInfos = other.GetType().GetProperties(BindingFlags.Public
| BindingFlags.Instance);
FieldInfo[] fis = other.GetType().GetFields(BindingFlags.Public |
BindingFlags.Instance);
foreach (FieldInfo fi in fis)
{
if ((fi.Attributes & FieldAttributes.FieldAccessMask) !=
FieldAttributes.Literal &&
(fi.Attributes & FieldAttributes.FieldAccessMask) !=
FieldAttributes.Static)
{
if (IsXmlIgnored(fi)) { continue; }
var myval = fi.GetValue(other);
fi.SetValue(target, myval);
}
}
foreach (PropertyInfo pi in propertyInfos)
{
if (!pi.CanWrite || !pi.CanRead) { continue; }
if (IsXmlIgnored(pi)) { continue; }
var myval = pi.GetValue(other, null);
pi.SetValue(target, myval, null);
}
}
private static bool IsXmlIgnored(MemberInfo pi)
{
object[] fiGetCustomAttributes = pi.GetCustomAttributes(false);
foreach (object ob in fiGetCustomAttributes)
{
if (ob.GetType().
Equals(typeof(System.Xml.Serialization.XmlIgnoreAttribute)))
{
return true;
}
}
return false;
}
}
// to use it ...
// the deserialization method of the singleton mySingleton
public bool loadSingleton()
{
bool ret= false;
try
{
Type myType = GetType();
XmlSerializer reader = new XmlSerializer(myType);
try
{
using (StreamReader file = new StreamReader(filename))
{
try
{
mySingleton t1 = (mySingleton)reader.Deserialize(file);
CopySerializationFields(t1);
ret= true;
}
catch
{
...
}
}
}
catch
{
...
}
}
catch (Exception ex)
{
...
}
return ret;
}
private void CopySerializationFields(ProcessingSettings other)
{
SerializationHelpers.CopyTypeFields(this, other);
}
I just searched quite long for a similar solution for atomic classes and have found an answer to a similar problem by Marc Gravell. This also works for singletons.
In your singleton class you implement the GetRealObject method defined by the interface System.Runtime.Serialization.IObjectReference. There you could also add previously serialized data to the singleton if needed and return the static reference as the reference that is used after deserialization.
Here's my example:
[System.Runtime.Serialization.DataContract]
public class MySingletonClass : System.Runtime.Serialization.IObjectReference
{
private MySingletonClass()
{
}
private static MySingletonClass _Instance;
public static MySingletonClass Instance
{
get
{
if (_Instance == null)
_Instance = new MySingletonClass();
return _Instance;
}
}
object System.Runtime.Serialization.IObjectReference.GetRealObject(System.Runtime.Serialization.StreamingContext context)
{
MySingletonClass realObject = Instance;
realObject.Merge(this);
return realObject;
}
private void Merge(MySingletonClass otherInstance)
{
// do your merging here
}
}
You can use this for atomic classes too. You only have to change the Instance property to a GetInstance method and call it with the appropriate property in the GetRealObject method (i.e. an ID).
[DataContract]
public sealed class SerializableSingletonPattern
{
public static SerializableSingletonPattern Instance { get; private set; } = new SerializableSingletonPattern();
[DataMember] public bool YourData { get; private set; }
// explicit static constructor so C# compiler will not mark type as beforefieldinit
static SerializableSingletonPattern() { }
SerializableSingletonPattern() // your constructor
{
}
[OnDeserialized]
void OnDeserialized(StreamingContext context)
{
Instance = this;
}
}

DataContract serialization of an inherited type into a base type

I'm trying to serialize class B as an instance of ita base class A. The DataContractSerializer won't allow me to do that.
An example failing the serialization is as follows:
class Program
{
[DataContract]
public class A
{
public int Id { get; set; }
}
[DataContract]
public class B : A
{
}
static void Main(string[] args)
{
A instance = new B { Id = 42 };
var dataContractSerializer = new DataContractSerializer(typeof(A));
var xmlOutput = new StringBuilder();
using (var writer = XmlWriter.Create(xmlOutput))
{
dataContractSerializer.WriteObject(writer, instance);
}
}
}
I know that the issue is easily resolved by added the KnownTypes attribute.
However I want to keep the class B hidden from the project (not add a reference).
Is it at all possible to achieve what I want? I tried the XmlSerializer but it gave me the same issue (it added the full instance type name in the XML) and is much more clunkier to use.
[DataContract(Name="YouCannotSeeMyName")]
[KnownTypes(typeof(B))]
public class B : A
And you will get
<A itype="YouCannotSeeMyName">
...
</A>
I'm pretty sure that you can't hide portions of the contract. This is akin to dealing with a web service where the contract has to be honored for each end to understand how and what to seriialize/deserialize.
In addition you can pass the B type within the DataContractSerializer versus using the attribute.
class Program
{
[DataContract]
public class A
{
public int Id { get; set; }
}
[DataContract]
public class B : A
{
}
static void Main(string[] args)
{
A instance = new B { Id = 42 };
var dataContractSerializer = new DataContractSerializer(typeof(A), new List<Type>() { typeof(B) });
var xmlOutput = new StringBuilder();
using (var writer = XmlWriter.Create(xmlOutput))
{
dataContractSerializer.WriteObject(writer, instance);
}
}
}
Which will give you...
<Program.A xmlns:i="http://www.w3.org/2001/XMLSchema-instance" i:type="Program.B"
xmlns="http://schemas.datacontract.org/2004/07/ConsoleApplication1" />

protobuf and List<object> - how to serialize / deserialize?

I have a List<object> with different types of objects in it like integers, strings, and custom types. All custom types are protobuf-adjusted.
What I wanna do now is to serialize / deserialize this list with protobuf.net. Up until now I suspect that I have to declare each and every type explicitly, which is unfortunately not possible with these mixed-list constructs. Because the binary formater has no problems to do these things I hope that I missed something and that you can help me out.
So my question is how to deal with objects in protobuf.net.
(disclosure: I'm the author of protobuf-net)
BinaryFormatter is a metadata-based serializer; i.e. it sends .NET type information about every object serialized. protobuf-net is a contract-based serializer (the binary equivalent of XmlSerializer / DataContractSerializer, which will also reject this).
There is no current mechanism for transporting arbitrary objects, since the other end will have no way of knowing what you are sending; however, if you have a known set of different object types you want to send, there may be options. There is also work in the pipeline to allow runtime-extensible schemas (rather than just attributes, which are fixed at build) - but this is far from complete.
This isn't ideal, but it works... it should be easier when I've completed the work to support runtime schemas:
using System;
using System.Collections.Generic;
using ProtoBuf;
[ProtoContract]
[ProtoInclude(10, typeof(DataItem<int>))]
[ProtoInclude(11, typeof(DataItem<string>))]
[ProtoInclude(12, typeof(DataItem<DateTime>))]
[ProtoInclude(13, typeof(DataItem<Foo>))]
abstract class DataItem {
public static DataItem<T> Create<T>(T value) {
return new DataItem<T>(value);
}
public object Value {
get { return ValueImpl; }
set { ValueImpl = value; }
}
protected abstract object ValueImpl {get;set;}
protected DataItem() { }
}
[ProtoContract]
sealed class DataItem<T> : DataItem {
public DataItem() { }
public DataItem(T value) { Value = value; }
[ProtoMember(1)]
public new T Value { get; set; }
protected override object ValueImpl {
get { return Value; }
set { Value = (T)value; }
}
}
[ProtoContract]
public class Foo {
[ProtoMember(1)]
public string Bar { get; set; }
public override string ToString() {
return "Foo with Bar=" + Bar;
}
}
static class Program {
static void Main() {
var items = new List<DataItem>();
items.Add(DataItem.Create(12345));
items.Add(DataItem.Create(DateTime.Today));
items.Add(DataItem.Create("abcde"));
items.Add(DataItem.Create(new Foo { Bar = "Marc" }));
items.Add(DataItem.Create(67890));
// serialize and deserialize
var clone = Serializer.DeepClone(items);
foreach (DataItem item in clone) {
Console.WriteLine(item.Value);
}
}
}
List<YourClass> list;
ProtoBuf.Serializer.Deserialize<List<YourClass>>(filestream);
There is a way of doing this, albeit not a very clean way, by using a wrapper object that utilises another serialisation mechanism that supports arbitrary objects. I am presenting an example below using JSON but, like I said, resorting to a different serialisation tool seems like defeating the purpose of using protobuf:
[DataContract]
public class ObjectWrapper
{
[DataMember(Order = 1)]
private readonly string _serialisedContent;
[DataMember(Order = 2)]
private readonly string _serialisedType;
public object Content { get; private set; }
[UsedImplicitly]
private ObjectWrapper() { }
public ObjectWrapper(object content)
{
_serialisedContent = JsonConvert.SerializeObject(content);
_serialisedType = content.GetType().FullName;
Content = content;
}
[ProtoAfterDeserialization]
private void Initialise()
{
var type = Type.GetType(_serialisedType);
Content = type != null
? JsonConvert.DeserializeObject(_serialisedContent, type)
: JsonConvert.DeserializeObject(_serialisedContent);
}
}
EDIT: This can also be done using C#'s built-in binary serialisation
[DataContract]
public class ObjectWrapper
{
[DataMember(Order = 1)]
private readonly string _serialisedContent;
public object Content { get; private set; }
[UsedImplicitly]
private ObjectWrapper() { }
public ObjectWrapper(object content)
{
using (var stream = new MemoryStream())
{
var formatter = new BinaryFormatter();
formatter.Serialize(stream, content);
stream.Flush();
stream.Position = 0;
_serialisedContent = Convert.ToBase64String(stream.ToArray());
}
}
[ProtoAfterDeserialization]
private void Initialise()
{
var data = Convert.FromBase64String(source);
using (var stream = new MemoryStream(data))
{
var formatter = new BinaryFormatter();
stream.Seek(0, SeekOrigin.Begin);
return formatter.Deserialize(stream);
}
}
}

Categories