System.Runtime.Serialization.Json.DataContractJsonSerializer.WriteObject ignores NonSerialized attribute while serializing - c#

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.

Related

XmlSerializer that serializes all and only nested value types properties

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

How to achieve ideal json serializing/deserializing in C# UWP?

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

Is there a way to serialize Dictionary containing abstract type using DataContractSerializer?

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.....

JSON deserialize List<SomeObject> is not working

We are deserializing the JSON using C# System.Runtime.Serialization.Json.DataContractJsonSerializer. It is working for normal objects but not for List.
For example if the json string is below then it works fine:
{"CacheInsertDateTime":"\/Date(1360761324878)\/","Data":{"__type":"SomeObject:#ConsoleApplication1","Symbol":"some string"}}
But if the josn is below:
{"CacheInsertDateTime":"\/Date(1360761324878)\/","Data":[{"__type":"SomeObject:#ConsoleApplication1","Symbol":"some string"},{"__type":"SomeObject:#ConsoleApplication1","Symbol":"some other string"}]}
The data comes as List<Object> not List<SomeObject>. I attached the sample solution also, showing the same problem. Any help or direction is appreciated.
EDIT: added the code
{
namespace ConsoleApplication1
{
class Program
{
/* Someobject class*/
[DataContract]
public class SomeObject
{
public SomeObject(string sym)
{
this.Symbol = sym;
}
[DataMember]
public string Symbol { get; set; }
}
/* Cahe Data */
[DataContract()]
[KnownType("GetKnownTypes")]
class CacheData
{
[DataMember()]
public object Data { get; set; }
[DataMember()]
public DateTime CacheInsertDateTime { get; set; }
public static IEnumerable<Type> GetKnownTypes(ICustomAttributeProvider provider)
{
return GetKnownTypes();
}
public static IEnumerable<Type> GetKnownTypes()
{
if (knownTypes == null)
{
// Since reflection is costly, we will do the lookup once for the known types and persist the data in knownTypes variable
knownTypes = new List<Type>();
// first add types from DataModel assembly get types which are marked with DataContract attribute
var typesInCurrentAssembly = Assembly.GetExecutingAssembly().GetTypes().Where
(t => t.GetCustomAttributes(false).Any(attrib => attrib is DataContractAttribute));
foreach (var type in typesInCurrentAssembly)
{
// add type and list<type> also to the known types list
knownTypes.Add(type);
knownTypes.Add(typeof(List<>).MakeGenericType(type));
}
knownTypes.Add(typeof(DataTable));
}
return knownTypes;
}
private static List<Type> knownTypes = null;
}
/*Cache Response Class*/
class CacheResponse<T> where T : class
{
public CacheData CacheData { get; set; }
public T Data
{
get{return (CacheData != null && CacheData.Data.GetType() == typeof(T)) ? CacheData.Data as T: null;}
}
}
/* Main class */
static void Main(string[] args)
{
//1. first with someobject, it works same as above
string jsonString = "{\"CacheInsertDateTime\":\"\\/Date(1360761324878)\\/\",\"Data\":{\"__type\":\"SomeObject:#ConsoleApplication1\",\"Symbol\":\"some object 1\"}}";
CacheData obj = null;
byte[] byteData = Encoding.UTF8.GetBytes(jsonString);
using (MemoryStream stream = new MemoryStream(byteData))
{
var serializer = new DataContractJsonSerializer(typeof(CacheData));
obj = serializer.ReadObject(stream) as CacheData;
}
CacheResponse<SomeObject> response1 = new CacheResponse<SomeObject>();
response1.CacheData = obj;
SomeObject responseObj = response1.Data; //this response object is fine
//2. with list<someobject>, it does not work
jsonString = "{\"CacheInsertDateTime\":\"\\/Date(1360761324878)\\/\",\"Data\":[{\"__type\":\"SomeObject:#ConsoleApplication1\",\"Symbol\":\"some object 1\"},{\"__type\":\"SomeObject:#ConsoleApplication1\",\"Symbol\":\"some object 2\"}]}";
byteData = Encoding.UTF8.GetBytes(jsonString);
using (MemoryStream stream = new MemoryStream(byteData))
{
var serializer = new DataContractJsonSerializer(typeof(CacheData));
obj = serializer.ReadObject(stream) as CacheData;
}
CacheResponse<List<SomeObject>> response2 = new CacheResponse<List<SomeObject>>();
response2.CacheData = obj;
List<SomeObject> responseList = response2.Data;//this is null
}
}
}
Make sure to let the serializer know what types to expect in the list, for example by passing it to the constructor of DataContractJsonSerializer.
The following example works as expected:
namespace ConsoleApplication1
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Runtime.Serialization.Json;
using System.Text;
public class SomeObject
{
public string Symbol { get; set; }
}
public class MyClass
{
public DateTime CacheInsertTime { get; set; }
public List<object> Data { get; set; }
}
public class Program
{
private const string JsonString = #"{""CacheInsertDateTime"":""\/Date(1360761324878)\/"",""Data"":[{""__type"":""SomeObject:#ConsoleApplication1"",""Symbol"":""some string""},{""__type"":""SomeObject:#ConsoleApplication1"",""Symbol"":""some other string""}]}";
private static void Main()
{
var ser = new DataContractJsonSerializer(typeof (MyClass), new[] {typeof (SomeObject)});
var ms = new MemoryStream(Encoding.ASCII.GetBytes(JsonString));
var obj = (MyClass) ser.ReadObject(ms);
Trace.Assert(obj.Data.Count == 2);
Trace.Assert(((SomeObject) obj.Data[1]).Symbol == "some other string");
}
}
}
Note how I pass typeof(SomeType) to the constructor of the serializer.

Assigning a default value to a property

I have an xml like so:
<Settings>
<User default="Programmer"></User>
<Level default="2"></Level>
<Settings>
This is deserialized to an object of type UserSettings:
[Serializable]
[XmlRoot("Settings")]
public class UserSettings
{
[XmlElement("User")]
public string User { get; set; }
[XmlElement("Level")]
public string Level { get; set; }
}
The UserSettings object gives whatever the values are there for the tags at runtime.
I want the class to return the default attribute value when either the tag is empty or the tag is absent in the incoming xml.
So if there is an object objUserSettings of type UserSettings then
objUserSettings.User
should give "Programmer", or whatever is in default attribute value in the xml if the tag User is empty.
Regards.
Adding another answer because I had some fun with this question. Take it or leave it, but this is probably how I would attack this feature.
Here's an answer that is more complicated, but it gives you type safety using generics and most of the heavy lifting is done in one base class (no need to copy/paste the same code over and over).
Added a property to UserSettings to show an example of another type...
[Serializable]
[XmlRoot("Settings")]
public class UserSettings
{
public UserSettings()
{
User = new DefaultableStringValue();
Level = new DefaultableIntegerValue();
IsFullscreen = new DefaultableBooleanValue();
}
[XmlElement("User")]
public DefaultableStringValue User { get; set; }
[XmlElement("Level")]
public DefaultableIntegerValue Level { get; set; }
[XmlElement("IsFullscreen")]
public DefaultableBooleanValue IsFullscreen { get; set; }
}
Simple implementations of typed DefaultableValues...
[Serializable]
public class DefaultableStringValue : DefaultableValue<string>
{
public DefaultableStringValue() : base(s => s) { }
}
[Serializable]
public class DefaultableIntegerValue : DefaultableValue<int>
{
public DefaultableIntegerValue() : base(int.Parse) { }
}
[Serializable]
public class DefaultableBooleanValue : DefaultableValue<bool>
{
public DefaultableBooleanValue() : base(bool.Parse) { }
}
Base class that does all of the heavy lifting of parsing and caching parsed values...
[Serializable]
public abstract class DefaultableValue<T>
{
protected Func<string, T> _parsingFunc;
private string _valueText;
private T _cachedValue;
private bool _isValueCached;
private string _defaultText;
private T _cachedDefault;
private bool _isDefaultCached;
protected DefaultableValue(Func<string, T> parsingFunc)
{
_parsingFunc = parsingFunc;
_isValueCached = false;
_isDefaultCached = false;
}
[XmlAttribute("default")]
public string DefaultText
{
get { return _defaultText; }
set
{
_defaultText = value;
_isDefaultCached = false;
}
}
[XmlText]
public string ValueText
{
get { return _valueText; }
set
{
_valueText = value;
_isValueCached = false;
}
}
[XmlIgnore]
public T Default
{
get
{
if (_isDefaultCached)
return _cachedDefault;
if (HasDefault)
return ParseAndCacheValue(DefaultText, out _cachedDefault, out _isDefaultCached);
return default(T);
}
set
{
DefaultText = value.ToString();
_cachedDefault = value;
_isDefaultCached = true;
}
}
[XmlIgnore]
public T Value
{
get
{
if (_isValueCached)
return _cachedValue;
if (HasValue)
return ParseAndCacheValue(ValueText, out _cachedValue, out _isValueCached);
return Default;
}
set
{
ValueText = value.ToString();
_cachedValue = value;
_isValueCached = true;
}
}
[XmlIgnore]
public bool HasDefault { get { return !string.IsNullOrEmpty(_defaultText); } }
[XmlIgnore]
public bool HasValue { get { return !string.IsNullOrEmpty(_valueText); } }
private T ParseAndCacheValue(string text, out T cache, out bool isCached)
{
cache = _parsingFunc(text);
isCached = true;
return cache;
}
}
And a sample program to demonstrate usage...
public class Program
{
private static void Main(string[] args)
{
UserSettings userSettings = new UserSettings();
userSettings.User.Default = "Programmer";
userSettings.Level.Default = 2;
userSettings.Level.Value = 99;
XmlSerializer xmlSerializer = new XmlSerializer(typeof(UserSettings));
string serializedUserSettings;
using (StringWriter stringWriter = new StringWriter())
{
xmlSerializer.Serialize(stringWriter, userSettings);
serializedUserSettings = stringWriter.GetStringBuilder().ToString();
}
UserSettings deserializedUserSettings;
using (StringReader stringReader = new StringReader(serializedUserSettings))
{
deserializedUserSettings = (UserSettings)xmlSerializer.Deserialize(stringReader);
}
Console.Out.WriteLine("User: HasDefault={0}, Default={1}, HasValue={2}, Value={3}",
deserializedUserSettings.User.HasDefault ? "Yes" : "No",
deserializedUserSettings.User.Default,
deserializedUserSettings.User.HasValue ? "Yes" : "No",
deserializedUserSettings.User.Value);
Console.Out.WriteLine("Level: HasDefault={0}, Default={1}, HasValue={2}, Value={3}",
deserializedUserSettings.Level.HasDefault ? "Yes" : "No",
deserializedUserSettings.Level.Default,
deserializedUserSettings.Level.HasValue ? "Yes" : "No",
deserializedUserSettings.Level.Value);
Console.Out.WriteLine("IsFullscreen: HasDefault={0}, Default={1}, HasValue={2}, Value={3}",
deserializedUserSettings.IsFullscreen.HasDefault ? "Yes" : "No",
deserializedUserSettings.IsFullscreen.Default,
deserializedUserSettings.IsFullscreen.HasValue ? "Yes" : "No",
deserializedUserSettings.IsFullscreen.Value);
Console.ReadLine();
}
}
Try this
using System.ComponentModel;
[Serializable]
[XmlRoot("Settings")]
public class UserSettings
{
[DefaultValue("Yogesh")]
[XmlElement("User")]
public string User { get; set; }
[DefaultValue("1st")]
[XmlElement("Level")]
public string Level { get; set; }
}
For more info see this.
You can use Default Value attribute for the property.
In you case it will be,
[Serializable]
[XmlRoot("Settings")]
public class UserSettings
{
[XmlElement("User")]
[DefaultValue("Programmer")]
public string User { get; set; }
[XmlElement("Level")]
[DefaultValue(2)]
public string Level { get; set; }
}
I don't believe there is a way to tell string to use that default xml attribute. You will have to deserialize each of those object into a structure that has the default value as a property which is an xml attribute.
Here's an example...
[Serializable]
[XmlRoot("Settings")]
public class UserSettings
{
[XmlElement("User")]
public DefaultableValue User { get; set; }
[XmlElement("Level")]
public DefaultableValue Level { get; set; }
}
[Serializable]
public class DefaultableValue
{
[XmlAttribute("default")]
public string Default { get; set; }
[XmlText]
public string Value { get; set; }
}
And sample program to demonstrate usage...
public class Program
{
private static void Main(string[] args)
{
UserSettings userSettings = new UserSettings();
userSettings.User = new DefaultableValue()
{
Default = "Programmer",
Value = "Tyler"
};
userSettings.Level = new DefaultableValue()
{
Default = "2",
Value = "99"
};
XmlSerializer xmlSerializer = new XmlSerializer(typeof(UserSettings));
string serializedUserSettings;
using (StringWriter stringWriter = new StringWriter())
{
xmlSerializer.Serialize(stringWriter, userSettings);
serializedUserSettings = stringWriter.GetStringBuilder().ToString();
}
UserSettings deserializedUserSettings;
using (StringReader stringReader = new StringReader(serializedUserSettings))
{
deserializedUserSettings = (UserSettings)xmlSerializer.Deserialize(stringReader);
}
Console.Out.WriteLine("User: Default={0}, Actual={1}",
deserializedUserSettings.User.Default,
deserializedUserSettings.User.Value);
Console.Out.WriteLine("Level: Default={0}, Actual={1}",
deserializedUserSettings.Level.Default,
deserializedUserSettings.Level.Value);
}
}
(Note that I have the default values in code, but they very well could have come from the xml file)

Categories

Resources