How can I deserialize JSON string into entity of type like this (self-tracking properties removed to be simple):
public class User:
{
int Id { get; set; }
string Name { get; set; }
public TrackableCollection<Role> Roles { get; set; } // <!
}
Role is also simple class with two properties. TrackableCollection is descendant of Collection (System.Collections.ObjectModel).
So I want: having JSON string like this
{"Id":0, "Name":"Test User", "Roles": [{"Id":1, "Name": "Role 1"}, {"Id":2, "Name": "Role 2"}, {"Id":3, "Name": "Role 3"}]}
get entity with correctly deserialized Roles collection.
Ok, seems like nobody was interested this question, anyway here is the solution. This class serializes and deserializes self-tracking POCO entities including all nested TrackableCollections and objects.
Please notice about SupportedTypes method. I added the IEntity interface (blank inside) and modified my T4 template on this line:
<#=Accessibility.ForType(entity)#> <#=code.SpaceAfter(code.AbstractOption(entity))#>partial class <#=code.Escape(entity)#><#=code.StringBefore(" : ", code.Escape(entity.BaseType))#><#=(entity.BaseType == null ? ": " : ", ") + "IEntity" #>, IObjectWithChangeTracker, INotifyPropertyChanged
You can do nothing about IEntity. Just write the SupportedTypes method as you need.
I also commented the [DataMember] attribute in the template above the ChangeTracker property:
//[DataMember]
public ObjectChangeTracker ChangeTracker
Anyway this is not important. Take the EntityConverter and enjoy.
/// <summary>
/// Serializes self-tracking POCO entities with DataMemberAttribute marked properties.
/// </summary>
public class EntityConverter : JavaScriptConverter
{
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
Dictionary<string, object> result = new Dictionary<string, object>();
if (obj != null)
{
var properties = obj.GetType().GetProperties().Where(prop => prop.HasAttibute(typeof(DataMemberAttribute)));
foreach (var property in properties)
{
object value = property.GetValue(obj, null);
// Serialize nested TrackableCollection object
if (property.PropertyType.Name.Equals(typeof(TrackableCollection<object>).Name))
value = SerializeCollection((value as IEnumerable).Cast<object>(), serializer);
result.Add(property.Name, value);
}
}
return result;
}
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
if (dictionary == null)
throw new ArgumentNullException("dictionary");
var entity = Activator.CreateInstance(type);
foreach (KeyValuePair<string, object> kvp in dictionary)
{
PropertyInfo property = type.GetProperty(kvp.Key);
if ((property != null) && (property.HasAttibute(typeof(DataMemberAttribute))))
{
object value = default(object);
if (!property.PropertyType.Name.Equals(typeof(TrackableCollection<object>).Name))
{
// If property is not a TrackableCollection object
// http://stackoverflow.com/questions/793714/how-can-i-fix-this-up-to-do-generic-conversion-to-nullablet
Type u = Nullable.GetUnderlyingType(property.PropertyType);
string jsonValue = kvp.Value != null ? kvp.Value.ToString() : null;
dynamic dynamicVal;
if (u != null)
dynamicVal = jsonValue == "null" ? null : Convert.ChangeType(jsonValue, u);
else if (kvp.Value is IDictionary<string, object>)
dynamicVal = Deserialize(kvp.Value as IDictionary<string, object>, property.PropertyType, serializer);
else
dynamicVal = Convert.ChangeType(jsonValue, property.PropertyType);
value = dynamicVal;
}
else
{
// If property is a TrackableCollection object
var dictionaries = (kvp.Value as IEnumerable).Cast<IDictionary<string, object>>();
value = DeserializeCollection(dictionaries, property.PropertyType, serializer);
}
property.SetValue(entity, value, null);
}
}
return entity;
}
/// <summary>
/// Serializes TrackableCollection
/// </summary>
protected IList<IDictionary<string, object>> SerializeCollection(IEnumerable<object> collection, JavaScriptSerializer serializer)
{
var result = new List<IDictionary<string, object>>();
foreach (object obj in collection)
{
result.Add(Serialize(obj, serializer));
}
return result;
}
/// <summary>
/// Deserializes TrackableCollection
/// </summary>
protected object DeserializeCollection(IEnumerable<IDictionary<string, object>> dictionaries, Type propertyType, JavaScriptSerializer serializer)
{
object collection = Activator.CreateInstance(propertyType); // TrackableCollection<T>
Type genericType = propertyType.GetGenericArguments()[0]; // T
MethodInfo addMethod = collection.GetType().GetMethod("Add"); // Add(T object)
foreach (var dic in dictionaries)
{
addMethod.Invoke(collection, new [] { Deserialize(dic, genericType, serializer) });
}
return collection;
}
/// <remarks>
/// http://stackoverflow.com/questions/159704/how-to-implement-custom-json-serialization-from-asp-net-web-service
/// </remarks>
public override IEnumerable<Type> SupportedTypes
{
get
{
IList<Type> result = new List<Type>();
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
{
AssemblyBuilder dynamicAssemblyCheck = assembly as AssemblyBuilder;
if (dynamicAssemblyCheck == null)
{
try
{
foreach (Type type in assembly.GetExportedTypes())
{
if ((type != typeof(IEntity)) && typeof(IEntity).IsAssignableFrom(type))
{
result.Add(type);
}
}
}
catch(Exception){} // bad practice, i know, i know
}
}
return result;
}
}
}
Related
How to serialize a class with Newtonsoft.Json.JsonConvert.SerializeObject to change empty List to null variable
my result from serialization
{"images":[]} or {"images":{}}
and would like to get
{"images":null}
my simple class
public class Images
{
[JsonProperty("images", Required = Required.Always, NullValueHandling = NullValueHandling.Include)]
public Images()
{
Parameters = new Parameters(); **// I know I could not initiate here only when I need it but I don't want to do it**
}
[JsonProperty("parameters", Required = Required.Always, NullValueHandling = NullValueHandling.Include)]
public Parameters Parameters { get; set; }
}
public class Parameters
{
[JsonProperty("id", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Include)]
public string Id { get; set; }
}
I did something like that
internal class NullEmptyConverter : JsonConverter
{
public override bool CanConvert(Type t) => t == typeof(GuidId);
public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null) return null;
var value = serializer.Deserialize<string>(reader);
return value;
}
public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
{
if (JsonConvert.SerializeObject(untypedValue, Formatting.None).ToString() == "[]")
{
serializer.Serialize(writer, null);
return;
}
if (JsonConvert.SerializeObject(untypedValue, Formatting.None).ToString() == "{}")
{
serializer.Serialize(writer, null);
return;
}
serializer.Serialize(writer,untypedValue);
return;
}
}
use
[JsonConverter (typeof (NullEmptyConverter))]
immediately before
public Parameters Parameters { get; set; }
It works, but maybe it can be done better, simpler or somewhere else something is wrong here?
I am banging my head hear on why my property ReCaptchaResponse JSONProperty will not bind to my model. The others do just find, and my JSON Value Provider class hits just fine. Any clue at all? It is always NULL.
Ajax Request
{"Name":"Joe","Email":"","Message":"","g-recaptcha-response":"data"}
ContactUsController.cs
[HttpPost]
public virtual ActionResult Index(ContactUsModel model)
{
_contactUsService.ContactUs(model);
return Json(new SuccessResponse("Submitted Successfully"));
}
ContactUsMode.cs
[JsonObject, DataContract]
public class ContactUsModel
{
public string Name { get; set; }
public string Email { get; set; }
public string Message { get; set; }
[JsonProperty(PropertyName = "g-recaptcha-response"), DataMember(Name = "g-recaptcha-response")]
public string ReCaptchaResponse { get; set; }
}
JsonNetValueProviderFactory.cs
namespace Tournaments.Models.Mvc
{
public class JsonNetValueProviderFactory : ValueProviderFactory
{
public override IValueProvider GetValueProvider(ControllerContext controllerContext)
{
// first make sure we have a valid context
if (controllerContext == null)
throw new ArgumentNullException("controllerContext");
// now make sure we are dealing with a json request
if (!controllerContext.HttpContext.Request.ContentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase))
return null;
// get a generic stream reader (get reader for the http stream)
var streamReader = new StreamReader(controllerContext.HttpContext.Request.InputStream);
// convert stream reader to a JSON Text Reader
var jsonReader = new JsonTextReader(streamReader);
// tell JSON to read
if (!jsonReader.Read())
return null;
// make a new Json serializer
var jsonSerializer = new JsonSerializer();
jsonSerializer.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
// add the dyamic object converter to our serializer
jsonSerializer.Converters.Add(new ExpandoObjectConverter());
// use JSON.NET to deserialize object to a dynamic (expando) object
Object jsonObject;
// if we start with a "[", treat this as an array
if (jsonReader.TokenType == JsonToken.StartArray)
jsonObject = jsonSerializer.Deserialize<List<ExpandoObject>>(jsonReader);
else
jsonObject = jsonSerializer.Deserialize<ExpandoObject>(jsonReader);
// create a backing store to hold all properties for this deserialization
var backingStore = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
// add all properties to this backing store
AddToBackingStore(backingStore, String.Empty, jsonObject);
// return the object in a dictionary value provider so the MVC understands it
return new DictionaryValueProvider<object>(backingStore, CultureInfo.CurrentCulture);
}
private static void AddToBackingStore(Dictionary<string, object> backingStore, string prefix, object value)
{
var d = value as IDictionary<string, object>;
if (d != null)
{
foreach (KeyValuePair<string, object> entry in d)
{
AddToBackingStore(backingStore, MakePropertyKey(prefix, entry.Key), entry.Value);
}
return;
}
var l = value as IList;
if (l != null)
{
for (int i = 0; i < l.Count; i++)
{
AddToBackingStore(backingStore, MakeArrayKey(prefix, i), l[i]);
}
return;
}
// primitive
backingStore[prefix] = value;
}
private static string MakeArrayKey(string prefix, int index)
{
return prefix + "[" + index.ToString(CultureInfo.InvariantCulture) + "]";
}
private static string MakePropertyKey(string prefix, string propertyName)
{
return (String.IsNullOrEmpty(prefix)) ? propertyName : prefix + "." + propertyName;
}
}
}
Try ModelBinder. ValueProviderFactory does not work because of ExpandoObject.
internal class JsonNetModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
controllerContext.HttpContext.Request.InputStream.Position = 0;
var stream = controllerContext.RequestContext.HttpContext.Request.InputStream;
var readStream = new StreamReader(stream, Encoding.UTF8);
var json = readStream.ReadToEnd();
return JsonConvert.DeserializeObject(json, bindingContext.ModelType);
}
}
ContactUsController.cs
[HttpPost]
public virtual ActionResult Index([ModelBinder(typeof(JsonNetModelBinder))]ContactUsModel model)
{
_contactUsService.ContactUs(model);
return Json(new SuccessResponse("Submitted Successfully"));
}
I have a question similar to Cannot deserialize JSON array into type - Json.NET, but I still get errors.
So, I have 3 classes:
public class Class1
{
public string[] P2 { get; set; }
}
public class Class2
{
public Wrapper<string>[] P2 { get; set; }
}
public class Wrapper<T>
{
public T Value { get; set; }
}
I am trying to serialize Class 2 into string and back into Class 1.
Here's how:
Class2 c2 = new Class2
{
P2 = new Wrapper<string>[]
{
new Wrapper<string> { Value = "a" },
new Wrapper<string> { Value = "a" },
},
};
string s = JsonConvert.SerializeObject(c2);
Class1 c1 = (Class1)JsonConvert.DeserializeObject(s, typeof(Class1), new FormatConverter());
FormatConverter class is defined below:
public class FormatConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (objectType == typeof(string[]))
{
List<string> list = new List<string>();
while (reader.Read())
{
if (reader.TokenType != JsonToken.StartObject)
{
continue;
}
Wrapper<string> obj = (Wrapper<string>)serializer.Deserialize(reader, typeof(Wrapper<string>));
list.Add(obj.Value);
}
return list.ToArray();
}
}
public override bool CanConvert(Type type)
{
if (type == typeof(string[]))
{
return true;
}
return false;
}
}
What am I missing? I get following exception:
An unhandled exception of type 'Newtonsoft.Json.JsonSerializationException' occurred in Newtonsoft.Json.dll
Additional information: Unexpected end when deserializing object. Path '', line 1, position 46.
Thanks,
Alex
I managed to find the answer myself. Here's how:
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (type == typeof(string[]) && reader.TokenType == JsonToken.StartArray)
{
//remove converter not to trigger a recursive call
var converter = serializer.Converters[0];
serializer.Converters.RemoveAt(0);
//deserialization into correct type
Wrapper<string>[] obj = (Wrapper<string>[])serializer.Deserialize(reader, typeof(Wrapper<string>[]));
//restore converter
serializer.Converters.Add(converter);
if (obj != null)
{
return obj.Select(w => w == null ? null : w.Value).ToArray();
}
return reader.Value;
}
}
I want to deserialize json which returns different data for different parameters.
Mostly I get:
{"posts": [{ "id" : 1, "name":"post1" },{ "id" : 1, "name":"post1" }]}
But sometimes the data returned is
{"posts": false}
I want to deserialize this as the following class
public class GetReaderResponse
{
public IEnumerable<ReaderPost> posts {get; set;}
}
public class ReaderPost
{
public int id {get; set;}
public string name{get; set;}
}
I am using C#,json.net but not able to do this correctly.
Newtonsoft.Json.JsonConvert.DeserializeObject<GetReaderResponse>(dataString);
You could build a custom converter, but an easy way to handle this would be to write an error handler that detects errors with the posts property:
var settings = new JsonSerializerSettings();
settings.Error += (sender, args) =>
{
if (string.Equals("posts", args.ErrorContext.Path, StringComparison.OrdinalIgnoreCase))
{
var currentObject = args.CurrentObject as GetReaderResponse;
currentObject.posts = Enumerable.Empty<ReaderPost>();
args.ErrorContext.Handled = true;
}
};
GetReaderResponse resp =
JsonConvert.DeserializeObject<GetReaderResponse>(json, settings);
This sets posts to Enumerable.Empty<ReaderPost>. This is still a little unsatisfying because if any error occurs, the property will be set. You could build a full custom converter to do this as a more complete solution.
Here's a converter that will take care of this:
public class PostsConverter : JsonConverter
{
public override object ReadJson(
JsonReader reader,
Type objectType,
object existingValue,
JsonSerializer serializer)
{
JToken val = JValue.ReadFrom(reader);
object result = null;
if (val.Type == JTokenType.Array)
{
result = val.ToObject<IEnumerable<ReaderPost>>();
}
else if (val.Type == JTokenType.Boolean)
{
result = Enumerable.Empty<ReaderPost>();
}
return result;
}
public override void WriteJson(
JsonWriter writer,
object value,
JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override bool CanConvert (Type type)
{
return typeof(IEnumerable<ReaderPost>).IsAssignableFrom(type);
}
public override bool CanRead
{
get { return true; }
}
}
Usage:
var settings = new JsonSerializerSettings();
settings.Converters = new [] { new PostsConverter() };
GetReaderResponse resp =
JsonConvert.DeserializeObject<GetReaderResponse>(json, settings);
Example: https://dotnetfiddle.net/i9CXwp
By using JSON.NETs built in LINQ to JSON, you can try someting like this:
JObject jObject = JObject.Parse(json);
GetReaderResponse response = new GetReaderResponse();
if (jObject["posts"] is JArray)
response = jObject.ToObject<GetReaderResponse>();
// Do something with the response object.
where json variable is the json string you need to deserialize.
try this:
public class GetReaderResponse
{
public bool posts { get; set; }
public ReaderPost[] post { get; set; }
}
After reading #Ilija's comment I think I might have found a answer. I did not want not use string literals so I modified my class GetReaderResponse to look like below:
public class GetReaderResponse
{
public dynamic posts {get; set;}
public IEnumerable<ReaderPost> Posts
{
get
{
if (posts is bool )
return new ReaderPost[0];
return posts.ToObject<IEnumerable<ReaderPost>>();
}
}
}
Does this sound fine or does it look messy?
I am serializing a collection of objects that contains a dictionary called dynamic properties.
The default Json emitted looks like this:
[{"dynamicProperties":{"WatchId":7771,"Issues":0,"WatchType":"x","Location":"Equinix Source","Name":"PI_5570_5580"}},
{"dynamicProperties":{"WatchId":7769,"Issues":0,"WatchType":"x","Location":"Equinix Source","Name":"PI_5570_5574"}},
{"dynamicProperties":{"WatchId":7767,"Issues":0,"WatchType":"x","Location":"Equinix Source","Name":"PI_5570_5572"}},
{"dynamicProperties":{"WatchId":7765,"Issues":0,"WatchType":"y","Location":"Equinix Source","Name":"highlight_SM"}},
{"dynamicProperties":{"WatchId":8432,"Issues":0,"WatchType":"y","Location":"Test Devices","Name":"Cisco1700PI"}}]
I'd like to produce Json that looks like this:
[{"WatchId":7771,"Issues":0,"WatchType":"x","Location":"Equinix Source","Name":"PI_5570_5580"},
{"WatchId":7769,"Issues":0,"WatchType":"x","Location":"Equinix Source","Name":"PI_5570_5574"},
{"WatchId":7767,"Issues":0,"WatchType":"x","Location":"Equinix Source","Name":"PI_5570_5572"},
{"WatchId":7765,"Issues":0,"WatchType":"y","Location":"Equinix Source","Name":"highlight_SM"},
{"WatchId":8432,"Issues":0,"WatchType":"y","Location":"Test Devices","Name":"Cisco1700PI"}]
From reading the Json.Net documentation it looks like I could build a CustomContractResolver for my class, but I cannot find any details on how to go about this... Can anyone shed any light on the direction I should be looking in?
The class I am trying to serialize is below.
I needed a class that allow dynamic style behaviour and is also serializable and deserializable over WCF.
[DataContract]
public class SerializableDynamicObject : IDynamicMetaObjectProvider
{
[DataMember]
private IDictionary<string, object> dynamicProperties = new Dictionary<string, object>();
#region IDynamicMetaObjectProvider implementation
public DynamicMetaObject GetMetaObject(Expression expression)
{
return new SerializableDynamicMetaObject(expression,
BindingRestrictions.GetInstanceRestriction(expression, this), this);
}
#endregion
#region Helper methods for dynamic meta object support
internal object setValue(string name, object value)
{
dynamicProperties.Add(name, value);
return value;
}
internal object getValue(string name)
{
object value;
if (!dynamicProperties.TryGetValue(name, out value))
{
value = null;
}
return value;
}
internal IEnumerable<string> getDynamicMemberNames()
{
return dynamicProperties.Keys;
}
#endregion
}
Implement ISerializable
[DataContract]
public class SerializableDynamicObject : IDynamicMetaObjectProvider, ISerializable
{
[DataMember]
private IDictionary<string, object> dynamicProperties = new Dictionary<string, object>();
#region IDynamicMetaObjectProvider implementation
public DynamicMetaObject GetMetaObject(Expression expression)
{
return new SerializableDynamicMetaObject(expression,
BindingRestrictions.GetInstanceRestriction(expression, this), this);
}
#endregion
#region Helper methods for dynamic meta object support
internal object setValue(string name, object value)
{
dynamicProperties.Add(name, value);
return value;
}
internal object getValue(string name)
{
object value;
if (!dynamicProperties.TryGetValue(name, out value))
{
value = null;
}
return value;
}
internal IEnumerable<string> getDynamicMemberNames()
{
return dynamicProperties.Keys;
}
#endregion
#region ISerializable Members
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
foreach (var key in dynamicProperties.Keys)
{
info.AddValue(key.ToString(), dynamicProperties[key]);
}
}
#endregion
}
Now test this code in a little console app...
static void Main(string[] args)
{
SerializableDynamicObject obj1 = new SerializableDynamicObject();
obj1.setValue("WatchId", 7771);
obj1.setValue("Issues", 0);
obj1.setValue("Location", "sample location1");
obj1.setValue("Name", "sample name 1");
SerializableDynamicObject obj2 = new SerializableDynamicObject();
obj2.setValue("WatchId", 7771);
obj2.setValue("Issues", 0);
obj2.setValue("Location", "sample location1");
obj2.setValue("Name", "sample name 1");
SerializableDynamicObject obj3 = new SerializableDynamicObject();
obj3.setValue("WatchId", 7771);
obj3.setValue("Issues", 0);
obj3.setValue("Location", "sample location1");
obj3.setValue("Name", "sample name 1");
SerializableDynamicObject[] dictArray = new
SerializableDynamicObject[] {
obj1, obj2, obj3
};
Newtonsoft.Json.JsonSerializer ser = new Newtonsoft.Json.JsonSerializer();
ser.Serialize(Console.Out, dictArray);
}
This is the output of the program:
[{"WatchId":7771,"Issues":0,"Location":"sample location1","Name":"sample name 1"},{"WatchId":7771,"Issues":0,"Location":"sample location2","Name":"sample name 2"},{"WatchId":7771,"Issues":0,"Location":"sample location3","Name":"sample name3"}]