Note: The question is restricted to C# UWP.
The Dream:
public static class Serializer {
// return json string
public string Serialize(object obj) { ??? }
// return T from json string
public T Deserialize<T>(string jsonString) { ??? }
}
Closest I've Come:
public static class Serializer
{
public static string Serialize(object obj, DataContractJsonSerializerSettings settings=null)
{
if (obj == null) {
throw new NullReferenceException();
}
settings = settings ?? new DataContractJsonSerializerSettings();
DataContractJsonSerializer jsonizer = new DataContractJsonSerializer(obj.GetType(), settings);
string jsonString = null;
using ( MemoryStream stream = new MemoryStream() )
{
jsonizer.WriteObject(stream, obj);
stream.Position = 0;
StreamReader sr = new StreamReader(stream);
jsonString = sr.ReadToEnd();
}
return jsonString;
}
public static T Deserialize<T>(string jsonString)
{
DataContractJsonSerializer jsonizer = new DataContractJsonSerializer(typeof(T));
T obj;
using (Stream stream = GenerateStreamFromString(jsonString))
{
obj = (T)jsonizer.ReadObject(stream);
}
return obj;
}
private static Stream GenerateStreamFromString(string s)
{
MemoryStream stream = new MemoryStream();
StreamWriter writer = new StreamWriter(stream);
writer.Write(s);
writer.Flush();
stream.Position = 0;
return stream;
}
}
The Problem
The partial solution I posted works in simple cases. However, it fails when the subtype of the object being deserialized is difficult (or impossible) to determine from the json string. For instance,
IList<Animal> animals = new List<Animal>();
animals.add(new Dog("Woofy"));
animals.add(new Cat("Fluffy"));
string json = Serializer.Serialize(animals);
IList<Animal> result = Serializer.Deserialize<List<Animal>>(json);
// ^ fails because subtype information was lost during serialization
bool succeeded = result.get(0).Name.Equals("Woofy") && result.get(1).Name.Equals("Fluffy");
What I'm Looking For:
An implementation of the skeleton specified in "The Dream" which passes the driver specified in "The Problem". Comments welcome.
Your Serializer works perfectly fine, if you add the KnownType-attributes to your base class:
[DataContract]
[KnownType(typeof(Dog))] // add these
[KnownType(typeof(Cat))] // lines
public class Animal
{
[DataMember]
public string Name { get; set; }
}
[DataContract]
public class Dog : Animal
{
}
[DataContract]
public class Cat : Animal
{
}
It's necessary for the DataContractJsonSerializer to preserve the type-information of the instances being serialized. You can see this in the resulting serialized JSON:
[{\"__type\":\"Dog:#My.Serialization.Sample.Project\",\"Name\":\"Woofy\"},{\"__type\":\"Cat:#My.Serialization.Sample.Project\",\"Name\":\"Fluffy\"}]
There is the extra key __type which holds the concrete information that the first object is a Dog form namespace My.Serialization.Sample.Project.
But as #dbc already mentioned, you might be slightly better off using JSON.NET which easily allows you to serialize your list without having the need to decorate your data transfer object with all those attributes. Even DataContract and DataMember are not needed.
public class Animal
{
public string Name { get; set; }
}
public class Dog : Animal { }
public class Cat : Animal { }
Using it this way
var settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto };
string jsonNet = JsonConvert.SerializeObject(animals, settings);
var jsonNetResult = JsonConvert.DeserializeObject<IList<Animal>>(jsonNet);
yields that result:
[{\"$type\":\"My.Serialization.Sample.Project.Dog, My.Assembly\",\"Name\":\"Woofy\"},{\"$type\":\"My.Serialization.Sample.Project.Cat, My.Assembly\",\"Name\":\"Fluffy\"}]
With JsonSubTypes you have at least two possibilities:
[JsonConverter(typeof(JsonSubtypes))]
[JsonSubtypes.KnownSubTypeWithProperty(typeof(Dog), "HadWalkToday")]
[JsonSubtypes.KnownSubTypeWithProperty(typeof(Cat), "ColorOfWhiskers")]
public class Animal
{
public string Name { get; set; }
}
public class Dog : Animal
{
public bool HadWalkToday { get; set; }
}
public class Cat : Animal
{
public string ColorOfWhiskers { get; set; }
}
or
[JsonConverter(typeof(JsonSubtypes), "Sound")]
[JsonSubtypes.KnownSubType(typeof(Dog), "Bark")]
[JsonSubtypes.KnownSubType(typeof(Cat), "Meow")]
public class Animal
{
public virtual string Sound { get; }
public string Color { get; set; }
}
public class Dog : Animal
{
public override string Sound { get; } = "Bark";
public string Breed { get; set; }
}
public class Cat : Animal
{
public override string Sound { get; } = "Meow";
public bool Declawed { get; set; }
}
That works with:
[Test]
public void Proof()
{
Dog dog = new Dog()
{
Name = "Woofy",
HadWalkToday = true
};
Cat cat = new Cat()
{
Name = "Fluffy",
ColorOfWhiskers = "Brown"
};
IList<Animal> animals = new List<Animal>()
{
dog,
cat
};
string json = JsonConvert.SerializeObject(animals);
IList<Animal> result = JsonConvert.DeserializeObject<List<Animal>>(json);
Assert.IsTrue(result[0].GetType() == typeof(Dog));
Assert.IsTrue(result[1].GetType() == typeof(Cat));
}
The answer I arrived at:
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
namespace SerializationDemo
{
class Program
{
static void Main(string[] args)
{
Dog dog = new Dog()
{
Name = "Woofy",
HadWalkToday = true
};
Cat cat = new Cat()
{
Name = "Fluffy",
ColorOfWhiskers = "Brown"
};
IList<Animal> animals = new List<Animal>()
{
dog,
cat
};
string json = Serializer.Serialize(animals);
IList<Animal> result = Serializer.Deserialize<List<Animal>>(json);
bool serializerSuccessful = dog.Equals(result[0]) && cat.Equals(result[1]);
}
}
public class Animal
{
public string Name { get; set; }
public override bool Equals(object obj)
{
var animal = obj as Animal;
return this.Name == animal.Name;
}
}
public class Dog : Animal
{
public bool HadWalkToday { get; set; }
public override bool Equals(object obj)
{
var dog = obj as Dog;
return this.HadWalkToday == dog.HadWalkToday && base.Equals(obj);
}
}
public class Cat : Animal
{
public string ColorOfWhiskers { get; set; }
public override bool Equals(object obj)
{
var cat = obj as Cat;
return this.ColorOfWhiskers == cat.ColorOfWhiskers && base.Equals(obj);
}
}
public static class Serializer
{
private static readonly JsonSerializerSettings settings = new JsonSerializerSettings()
{
TypeNameHandling = TypeNameHandling.All,
Formatting = Formatting.Indented
};
public static string Serialize(object obj)
{
if (obj == null)
{
throw new NullReferenceException();
}
string jsonString = JsonConvert.SerializeObject(obj, settings);
return jsonString;
}
public static T Deserialize<T>(string jsonString)
{
T obj = JsonConvert.DeserializeObject<T>(jsonString, settings);
return obj;
}
}
}
Related
We have a complex issue in our system regarding retaining polymorphism after we have saved a config file to the database.
We have simplified the problem for the sake of this question. See code below.
Here is the code:
class Wheel
{
public virtual string GetWidth()
{
return "Normal";
}
}
class SmallWheel : Wheel
{
public override string GetWidth()
{
return "Small";
}
}
class BigWheel : Wheel
{
public override string GetWidth()
{
return "Big";
}
}
public static async Task Main(string[] args)
{
//Build list of wheels
var wheels = new List<Wheel>()
{
new Wheel(),
new SmallWheel(),
new BigWheel(),
};
//We print wheels to check it works
foreach (var x in wheels)
{
Console.WriteLine(x.GetWidth());
}
//Save the list to the db as a string
var file = JsonConvert.SerializeObject(wheels);
//We just use file instead of db for simplictiydf
//Later we read the config file from the DB
var wheelsFromDb = JsonConvert.DeserializeObject<List<Wheel>>(file);
//We now want to print out our wheels.
Console.WriteLine("Printing WHeels from Db");
foreach (var x in wheelsFromDb)
{
Console.WriteLine(x.GetWidth());
}
}
Results when I run it:
Normal
Small
Big
Printing WHeels from Db
Normal
Normal
Normal
Now as you can see we are losing what type the wheel is after de-serialisation.
How can we go about solving this issue?
In essence, we need to store a config file with multiple children classes each with overrides functions. I realise that when Json deserializes the raw string it has no context of what type the class was before. Is there a way we can store that context or use a different library or database?
I am using this code for a list of derived classes. You can use a base class instead of an interface as well
IAnimal[] animals = new IAnimal[] {
new Cat{CatName="Tom"},
new Dog{DogName="Scoopy"},
new Rabbit{RabitName="Honey"}
};
var jsonSerializerSettings = new JsonSerializerSettings()
{
TypeNameHandling = TypeNameHandling.All
};
var json = JsonConvert.SerializeObject(animals, jsonSerializerSettings);
List<IAnimal> animalsBack = ((JArray)JsonConvert.DeserializeObject(json)).Select(o => (IAnimal)JsonConvert.DeserializeObject(o.ToString(), Type.GetType((string)o["$type"]))).ToList();
classes
public interface IAnimal
{
}
public class Animal : IAnimal
{
}
public class Cat : IAnimal { public string CatName { get; set; } }
public class Dog : IAnimal { public string DogName { get; set; } }
public class Rabbit : IAnimal { public string RabitName { get; set; } }
JsonConvert creates property $type under the hood when TypeNameHandling.All is used.
So we can give a clue to compiler about what type actually is by creating Size property in classes:
public class Wheel
{
public virtual string Size { get; set; } = WheelSize.NORMAL;
public virtual string GetWidth() => Size;
}
public class SmallWheel : Wheel
{
public override string Size { get; set; } = WheelSize.SMALL;
}
public class BigWheel : Wheel
{
override public string Size { get; set; } = WheelSize.BIG;
}
This is a class which contains size of wheels:
public class WheelSize
{
public static string SMALL = "small";
public static string NORMAL = "normal";
public static string BIG = "big";
}
So far, so good. But how we can deserialize json based on size wheel? We can write custom deserializer:
internal class WheelJsonConverter : JsonConverter
{
private readonly Type[] _types;
public WheelJsonConverter(params Type[] types)
{
_types = types;
}
public override object ReadJson(JsonReader reader, Type objectType,
object existingValue, JsonSerializer serializer)
{
List<Wheel> wheels = new List<Wheel>();
WheelToSize wheelToSize = new WheelToSize();
JArray jArray = JArray.Load(reader);
foreach (JToken token in jArray)
{
string wheelSize = token["size"].ToString();
Wheel wheel = wheelToSize.WheelBySize[wheelSize];
wheels.Add(wheel);
}
return wheels;
}
public override bool CanConvert(Type objectType)
{
if (objectType.IsGenericType
&& IsGenericTypeArgumentTheSame(objectType))
return true;
return false;
}
private bool IsGenericTypeArgumentTheSame(Type objectType) =>
objectType.GenericTypeArguments[0] == _types[0];
public override void WriteJson(JsonWriter writer, object? value,
JsonSerializer serializer)
{
JToken t = JToken.FromObject(value);
if (t.Type != JTokenType.Object)
{
t.WriteTo(writer);
}
else
{
JObject o = (JObject)t;
o.WriteTo(writer);
}
}
}
This is a class to create json in lower case:
public class LowercaseContractResolver : DefaultContractResolver
{
protected override string ResolvePropertyName(string propertyName)
{
return propertyName.ToLower();
}
}
This is a factory which creates an object based on size:
public class WheelToSize
{
public Dictionary<string, Wheel> WheelBySize { get; private set; } = new()
{
{ WheelSize.NORMAL, new Wheel() },
{ WheelSize.BIG, new BigWheel() },
{ WheelSize.SMALL, new SmallWheel() }
};
}
And then you can run code like this:
List<Wheel> wheels = new List<Wheel>()
{
new Wheel(),
new SmallWheel(),
new BigWheel(),
};
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.ContractResolver = new LowercaseContractResolver();
var file = JsonConvert.SerializeObject(wheels, Formatting.Indented, settings);
var wheelsFromDb = JsonConvert.DeserializeObject<List<Wheel>>(file,
new WheelJsonConverter(typeof(Wheel)));
//We now want to print out our wheels.
Console.WriteLine("Printing WHeels from Db");
foreach (var x in wheelsFromDb)
Console.WriteLine(x.GetWidth());
I need to serialize a class so that the serialization will include all nested value types properties.
I found it somewhat hard to generalize it in English (Not a native speaker, so an edit to the wording is welcomed), so I'll explain:
If the property is of a value type - serialize its name and value
If the property is of a Nullable type: If its value is non-null, do as above (Effectively, serialize the Nullable's Value property); else, don't serialize it.
If the property is of a class type, serialize the class' properties according to the above, and do not serialize the class name.
For example, this:
public class SerializeMe
{
public int A { get; set; }
public int? B { get; set; }
public int? C { get; set; }
public MyClass MyClass { get; set;}
}
public class MyClass
{
public int Z { get; set;}
}
If instantiated like this:
public static void Main()
{
var instance = new SerializeMe
{
A = 1,
B = 3,
MyClass = new MyClass { Z = 2},
});
}
Should be serialized like this:
<SerializeMe>
<A>1</A>
<B>3</B>
<Z>2</Z>
</SerializeMe>
But I don't know how to do the last bullet, and I end with:
<SerializeMe>
<A>1</A>
<B>3</B>
<UndesiredTag><Z>2</Z></UndesiredTag>
</SerializeMe>
Now, the last bullet requirement invites recursion, but as I understand from this answer, it's the parent class' WriteXml that may be able to omit the <UndesiredTag> tag, while the nested class can't.
So, what I currently have (fiddle):
using System;
using System.Reflection;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
using System.IO;
public class SerializeMe : IXmlSerializable
{
public int A { get; set; }
public int? B { get; set; }
public int? C { get; set; }
public MyClass MyClass { get; set;}
public void WriteXml(XmlWriter writer)
{
Program.WriteXml<SerializeMe>(writer, this);
}
public void ReadXml(XmlReader reader) {}
public XmlSchema GetSchema() { return null; }
}
[AttributeUsage(AttributeTargets.Class)]
public class Nested : Attribute
{}
[Nested]
public class MyClass : IXmlSerializable
{
public int Z { get; set;}
public void WriteXml(XmlWriter writer)
{
Program.WriteXml<MyClass>(writer, this);
}
public void ReadXml(XmlReader reader) {}
public XmlSchema GetSchema() { return null; }
}
public class Program
{
public static void Main()
{
var s = XmlSerialize<SerializeMe>(new SerializeMe
{
A = 1,
B = 3,
MyClass = new MyClass { Z = 2},
});
Console.WriteLine(s);
}
public static string XmlSerialize<T>(T entity) where T : class
{
XmlWriterSettings settings = new XmlWriterSettings();
settings.OmitXmlDeclaration = true;
XmlSerializer xsSubmit = new XmlSerializer(typeof(T));
StringWriter sw = new StringWriter();
using (XmlWriter writer = XmlWriter.Create(sw, settings))
{
var xmlns = new XmlSerializerNamespaces();
xmlns.Add(string.Empty, string.Empty);
xsSubmit.Serialize(writer, entity, xmlns);
return sw.ToString();
}
}
public static void WriteXml<T>(XmlWriter writer, T obj)
{
PropertyInfo[] props = obj.GetType().GetProperties();
foreach (var prop in props)
{
var val = prop.GetValue(obj);
if (val != null)
{
if (prop.PropertyType.IsValueType ||
prop.PropertyType.IsGenericType &&
prop.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
{
writer.WriteElementString(prop.Name, val.ToString());
}
else
{
if (prop.PropertyType.GetCustomAttribute(typeof(Nested)) != null)
{
writer.WriteStartElement("UndesiredTag"); // If only I could use an empty string...
((dynamic)val).WriteXml(writer);
writer.WriteEndElement();
}
}
}
}
}
}
Note that my current code assumes only one level of nesting. If you think you can solve my issue using a recursion, it would be better - since you'd allow multiple nesting levels.
Since you override all default serialization anyway, it seems simpler to me to just ditch XmlSerializer and do it completely on your own.
public static void Main()
{
var s = XmlSerialize(new SerializeMe
{
A = 1,
B = 3,
MyClass = new MyClass { Z = 2 },
});
Console.WriteLine(s);
}
public static string XmlSerialize(object entity)
{
var buf = new StringBuilder();
using (var writer = XmlWriter.Create(buf, new XmlWriterSettings() {
OmitXmlDeclaration = true,
Indent = true
}))
{
WriteElement(writer, entity);
}
return buf.ToString();
}
static void WriteElement(XmlWriter writer, object obj)
{
writer.WriteStartElement(obj.GetType().Name);
WriteElementProperties(writer, obj);
writer.WriteEndElement();
}
static void WriteElementProperties(XmlWriter writer, object obj)
{
foreach (var prop in obj.GetType().GetProperties())
{
var val = prop.GetValue(obj);
if (val != null)
{
if (prop.PropertyType.IsValueType ||
prop.PropertyType.IsGenericType &&
prop.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
{
writer.WriteElementString(prop.Name, val.ToString());
}
else
{
if (prop.PropertyType.GetCustomAttribute(typeof(Nested)) != null)
{
WriteElementProperties(writer, val);
} else {
WriteElement(writer, val);
}
}
}
}
}
For example
[DataContract]
public abstract class BaseClass
{
public abstract string id { get; set; }
}
[DataContract(Name = "class1")]
public class concreteClass1 : BaseClass
{
public concreteClass1() { }
[DataMember]
public override string id { get; set; }
[DataMember]
public string prop1 { get; set; }
[DataMember]
public string prop2 { get; set; }
}
[DataContract(Name = "class2")]
public class concreteClass2 : BaseClass
{
public concreteClass2() { }
[DataMember]
public override string id { get; set; }
[DataMember]
public string prop1 { get; set; }
[DataMember]
public string prop2 { get; set; }
}
When I try to serialize a dictionary containing one of the concrete classes like this
static public void Main(string[] args){
Dictionary<string, BaseClass> items = new Dictionary<string, BaseClass>();
items.Add("1", new concreteClass1() { id = "1", prop1 = "blah1" });
items.Add("11", new concreteClass1() { id = "11", prop1 = "blah11" });
var serializer = new DataContractSerializer(items.GetType());
string xmlString = string.Empty;
using (var sw = new StringWriter())
{
using (var writer = new XmlTextWriter(sw))
{
writer.Formatting = System.Xml.Formatting.Indented;
serializer.WriteObject(writer, items );
writer.Flush();
xmlString = sw.ToString();
}
}
}
I get this error when trying to WriteObject
Type 'ConsoleTest.Program+Base' cannot be serialized. Consider marking
it with the DataContractAttribute attribute, and marking all of its
members you want serialized with the DataMemberAttribute attribute.
If the type is a collection, consider marking it with the
CollectionDataContractAttribute. See the Microsoft .NET Framework
documentation for other supported types.
Is there a way to solve this?
EDIT: I also tried using KnownType on the base class but it didn't work
[DataContract]
[KnownType(typeof(concreteClass1))]
[KnownType(typeof(concreteClass2))]
public abstract class BaseClass
{
public abstract string id { get; set; }
}
try this...
using ....
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization;
using System.Xml;
classes...
[DataContract]
[KnownType(typeof(concreteClass1))]
[KnownType(typeof(concreteClass2))]
public abstract class BaseClass
{
[DataMember]
public abstract string id { get; set; }
}
[DataContract(Name = "class1")]
public class concreteClass1 : BaseClass
{
public concreteClass1() { }
[DataMember]
public override string id { get; set; }
[DataMember]
public string prop1 { get; set; }
[DataMember]
public string prop2 { get; set; }
}
[DataContract(Name = "class2")]
public class concreteClass2 : BaseClass
{
public concreteClass2() { }
[DataMember]
public override string id { get; set; }
[DataMember]
public string prop1 { get; set; }
[DataMember]
public string prop2 { get; set; }
}
code....
static void Main(string[] args)
{
Dictionary<string, BaseClass> items = new Dictionary<string, BaseClass>();
items.Add("1", new concreteClass1() { id = "1", prop1 = "blah1" });
items.Add("11", new concreteClass1() { id = "11", prop1 = "blah11" });
// this should work too....
items.Add("999", new concreteClass2() { id = "999", prop1 = "blah999" });
items.Add("888", new concreteClass2() { id = "888", prop1 = "blah888" });
//Serialize(items);
var serializer = new DataContractSerializer(items.GetType());
string xmlString = string.Empty;
try
{
using (var sw = new StringWriter())
{
using (var writer = new XmlTextWriter(sw))
{
writer.Formatting = System.Xml.Formatting.Indented;
serializer.WriteObject(writer, items);
writer.Flush();
xmlString = sw.ToString();
}
}
}
catch (Exception)
{
throw;
}
}
/////////////////////
UPDATE
/////////////////////
As a bit of a bonus (don't feel I eared my 25 points yet) here are two functions that will serialise and deserialise a generic object.....
public static void Serialize<T>(T data)
{
try // try to serialize the collection to a file
{
using (Stream stream = File.Open("data.xml", FileMode.Create))
{
// create DataContractSerializer
DataContractSerializer serializer = new DataContractSerializer(typeof (T));
// serialize the collection (EmployeeList1) to file (stream)
serializer.WriteObject(stream, data);
}
}
catch (IOException)
{
}
}
public static T Deserialize<T>() where T : new()
{
// Create an instance of T
T ReturnListOfT = CreateInstance<T>();
// Try to Deserialize from file stream
try
{
using (Stream stream = File.Open("data.xml", FileMode.Open))
{
// create DataContractSerializer
DataContractSerializer serializer = new DataContractSerializer(typeof (T));
// deserialize the collection (Employee) from file (stream)
ReturnListOfT = (T)serializer.ReadObject(stream);
}
}
catch (IOException)
{
}
return (T)ReturnListOfT;
}
// function to create instance of T
public static T CreateInstance<T>() where T : new()
{
return (T)Activator.CreateInstance(typeof(T));
}
instead of having to modify the XML manually, you could deserialise the object (from a file, in the example 'data.xml') using your existing classes and create a User Interface to allow the user to modify the properties of the object\classes, then re-save\ serialise the modified object back to a file.....
If I have something like:
public abstract class Animal { }
[XmlRoot]
public class Dog:Animal { }
[XmlRoot]
Public class Cat:Animal { }
How do I go about serializing (and then be able to de-serialize) a Cat object like:
<Cat> </Cat>
and not something like:
<Animal type="Cat"> </Animal>
I've tried using different combinations from the System.Xml.Serialization and System.Runtime.Serialization namespaces, but I can't seem to get it.
I can get a serialized object to look the way I want by specifying the type of the object in the serializer. This works for serialization, but not for de-serialization..because I don't know the type of object in the xml.
One possible solution would be:
public abstract class Animal
{
static Dictionary<String,Type> typeDic;
static Animal()
{
typeDic = new Dictionary<string, Type>();
//Get classes in the same namespace of this object
Type ctype = typeof(Animal);
Type[] types = ctype.Assembly.GetTypes().Where(t => String.Equals(t.Namespace, ctype.Namespace, StringComparison.Ordinal)).ToArray();
//For any XmlRootAttribute objects, add the ElementName and Type to a dictionary
foreach(Type type in types)
{
foreach (XmlRootAttribute xmlRoot in type.GetCustomAttributes(typeof(XmlRootAttribute), false))
{
typeDic.Add(xmlRoot.ElementName, type);
}
}
}
public static Content read(String XML)
{
XmlSerializer s = null;
//check the first element to see what the name is, then create the serializer using the type from the dictionary
XmlReader reader = XmlReader.Create(GenerateStreamFromString(XML));
reader.Read();
if (reader.Name == "xml")
{
while (reader.MoveToContent() != XmlNodeType.Element) { }
}
if (typeDic.ContainsKey(reader.Name))
s = new XmlSerializer(typeDic[reader.Name]);
else
throw new Exception("Unknown Type in read");
return (Content)s.Deserialize(reader);
}
public static string write<T>(T f)
{
XmlSerializer s = new XmlSerializer(typeof(T));
MemoryStream stream = new MemoryStream();
s.Serialize(stream, f);
stream.Position = 0;
return StreamToString(stream);
}
}
[XmlRoot(ElementName="Dog")]
public class Dog:Animal { }
[XmlRoot(ElementName="Cat")]
Public class Cat:Animal { }
I tried this for a long time and could not figure it out either. the only thing i could come up with is using some reflection and generating the doc myself:
class Program
{
static void Main(string[] args)
{
List<Animal> animals = new List<Animal>
{
new Dog{Name = "Ernie", HasFleas = true},
new Cat{ Name = "Bert", Collar = "Blue with a little bell" }
};
XDocument doc = new XDocument();
doc.Declaration = new XDeclaration("1.0","utf-8","true");
doc.Add(new XElement("root", animals.Select(animal => animal.GetElement)));
Console.WriteLine(doc);
}
}
public abstract class Animal
{
[XmlAttribute]
public string Name { get; set; }
public XElement GetElement
{
get
{
Type type = this.GetType();
XElement result = new XElement(type.Name);
foreach (PropertyInfo property in
type.GetProperties().Where(pi=> pi.CustomAttributes.Any(ca=> ca.AttributeType == typeof(System.Xml.Serialization.XmlAttributeAttribute))))
{
result.Add(new XAttribute(property.Name,property.GetValue(this)));
}
foreach (PropertyInfo property in
type.GetProperties().Where(pi => pi.CustomAttributes.Any(ca => ca.AttributeType == typeof(System.Xml.Serialization.XmlElementAttribute))))
{
result.Add(new XElement(property.Name,property.GetValue(this)));
}
return result;
}
}
}
public class Dog : Animal
{
[XmlAttribute]
public bool HasFleas { get; set; }
}
public class Cat : Animal
{
[XmlElement]
public string Collar { get; set; }
}
To deserialize you have to do the other way round so some form of factory.
If i try to serialize an object of the following ClassToSerialize class with System.Runtime.Serialization.Json.DataContractJsonSerializer
[DataContract,Serializable]
public class ClassToSerialize
{
[NonSerialized] private bool _mf;
public bool IsMf
{
get { return _mf};
set{ _mf = value;}
}
[DataMember]
public char PrimaryExc { get; set; }
}
public class TestClass
{
ClassToSerialize obj = new ClassToSerialize{PrimaryExchange = 'a', NoResults = true};
var serializer = new System.Runtime.Serialization.Json.DataContractJsonSerializer(ClassToSerialize);
var ms = new MemoryStream();
serializer.WriteObject(ms, obj);
return Encoding.UTF8.GetString(ms.ToArray());
}
The return string still contains IsMf property and its value. The NOnSerialized attribute is ignored. Can someone please suggest what attribute to use when using DataContractJsonSerializer so as to not serialize some properties
The following code worked for me (it's almost identical to your's with a few small compilation errors fixed):
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Text;
class Program
{
static void Main()
{
var obj = new ClassToSerialize
{
PrimaryExc = 'a',
NoResults = true
};
var serializer
= new DataContractJsonSerializer(typeof(ClassToSerialize));
var ms = new MemoryStream();
serializer.WriteObject(ms, obj);
Console.WriteLine(Encoding.UTF8.GetString(ms.ToArray()));
}
}
[DataContract]
[Serializable]
public class ClassToSerialize
{
[NonSerialized]
private bool _mf;
public bool IsMf
{
get { return _mf; }
set { _mf = value; }
}
[DataMember]
public bool NoResults { get; set; }
[DataMember]
public char PrimaryExc { get; set; }
}
Output:
{"NoResults":true,"PrimaryExc":"a"}
No, it doesn't contain it. You must be mistaken:
[DataContract]
public class ClassToSerialize
{
[NonSerialized]
private bool _mf;
public bool IsMf
{
get { return _mf; }
set{ _mf = value;}
}
[DataMember]
public char PrimaryExc { get; set; }
}
class Program
{
static void Main()
{
var obj = new ClassToSerialize
{
PrimaryExc = 'a',
IsMf = false
};
var serializer = new DataContractJsonSerializer(obj.GetType());
serializer.WriteObject(Console.OpenStandardOutput(), obj);
}
}
Output:
{"PrimaryExc":"a"}
Remark: You don't need the [Serializable] attribute on your class. That's only for binary serialization.