C# Automatic Property Deserialization of JSON

C# automatic property deserialization of JSON

What's happening here is the deserializer is trying to guess the name of your backing fields.
You can solve this by adding explicit mappings (DataContract/DataMember attributes) like this:

[DataContract]
public class Cat
{
[DataMember]
public string Name { get; set; }

[DataMember]
public string Breed { get; set; }
}

Deserializing JSON to C# class where property names in the JSON are dynamic

Since each object has a player_id that matches the object key values, you don't need to worry about capturing the object keys. I'd just serialize it to a Dictionary first, and then just take the values from that dictionary.

var players = JsonConvert.DeserializeObject<Dictionary<string, Player>>(input).Values;

How to set up constructors for deserialization of Get only properties without having to duplicate code in c#?

You could make the id argument nullable:

abstract class Box
{
public Box(double panelThickness) : this(null, panelThickness) { }

protected Box(int? id, double panelThickness)
{
ID = id ?? IDGenerator.GetNewID();
PanelThickness = panelThickness;
}

public int ID { get; }
public double PanelThickness { get; }
}

class RectangularBox : Box
{
private static double _rectPanelThickness = 0.2;

public RectangularBox(double xDimension, double yDimension)
: this(null, xDimension, yDimension) { }

[JsonConstructor]
private RectangularBox(int? id, double xDimension, double yDimension)
: base(id, _rectPanelThickness)
{
XDimension = xDimension;
YDimension = yDimension;
}

public double XDimension { get; }
public double YDimension { get; }
}

Obviously if your JSON does not contain an id then a new id will be generated, but that seems like something within your control.

Deserialize JSON into C# dynamic object?

If you are happy to have a dependency upon the System.Web.Helpers assembly, then you can use the Json class:

dynamic data = Json.Decode(json);

It is included with the MVC framework as an additional download to the .NET 4 framework. Be sure to give Vlad an upvote if that's helpful! However if you cannot assume the client environment includes this DLL, then read on.


An alternative deserialisation approach is suggested here. I modified the code slightly to fix a bug and suit my coding style. All you need is this code and a reference to System.Web.Extensions from your project:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Dynamic;
using System.Linq;
using System.Text;
using System.Web.Script.Serialization;

public sealed class DynamicJsonConverter : JavaScriptConverter
{
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
if (dictionary == null)
throw new ArgumentNullException("dictionary");

return type == typeof(object) ? new DynamicJsonObject(dictionary) : null;
}

public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
throw new NotImplementedException();
}

public override IEnumerable<Type> SupportedTypes
{
get { return new ReadOnlyCollection<Type>(new List<Type>(new[] { typeof(object) })); }
}

#region Nested type: DynamicJsonObject

private sealed class DynamicJsonObject : DynamicObject
{
private readonly IDictionary<string, object> _dictionary;

public DynamicJsonObject(IDictionary<string, object> dictionary)
{
if (dictionary == null)
throw new ArgumentNullException("dictionary");
_dictionary = dictionary;
}

public override string ToString()
{
var sb = new StringBuilder("{");
ToString(sb);
return sb.ToString();
}

private void ToString(StringBuilder sb)
{
var firstInDictionary = true;
foreach (var pair in _dictionary)
{
if (!firstInDictionary)
sb.Append(",");
firstInDictionary = false;
var value = pair.Value;
var name = pair.Key;
if (value is string)
{
sb.AppendFormat("{0}:\"{1}\"", name, value);
}
else if (value is IDictionary<string, object>)
{
new DynamicJsonObject((IDictionary<string, object>)value).ToString(sb);
}
else if (value is ArrayList)
{
sb.Append(name + ":[");
var firstInArray = true;
foreach (var arrayValue in (ArrayList)value)
{
if (!firstInArray)
sb.Append(",");
firstInArray = false;
if (arrayValue is IDictionary<string, object>)
new DynamicJsonObject((IDictionary<string, object>)arrayValue).ToString(sb);
else if (arrayValue is string)
sb.AppendFormat("\"{0}\"", arrayValue);
else
sb.AppendFormat("{0}", arrayValue);

}
sb.Append("]");
}
else
{
sb.AppendFormat("{0}:{1}", name, value);
}
}
sb.Append("}");
}

public override bool TryGetMember(GetMemberBinder binder, out object result)
{
if (!_dictionary.TryGetValue(binder.Name, out result))
{
// return null to avoid exception. caller can check for null this way...
result = null;
return true;
}

result = WrapResultObject(result);
return true;
}

public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)
{
if (indexes.Length == 1 && indexes[0] != null)
{
if (!_dictionary.TryGetValue(indexes[0].ToString(), out result))
{
// return null to avoid exception. caller can check for null this way...
result = null;
return true;
}

result = WrapResultObject(result);
return true;
}

return base.TryGetIndex(binder, indexes, out result);
}

private static object WrapResultObject(object result)
{
var dictionary = result as IDictionary<string, object>;
if (dictionary != null)
return new DynamicJsonObject(dictionary);

var arrayList = result as ArrayList;
if (arrayList != null && arrayList.Count > 0)
{
return arrayList[0] is IDictionary<string, object>
? new List<object>(arrayList.Cast<IDictionary<string, object>>().Select(x => new DynamicJsonObject(x)))
: new List<object>(arrayList.Cast<object>());
}

return result;
}
}

#endregion
}

You can use it like this:

string json = ...;

var serializer = new JavaScriptSerializer();
serializer.RegisterConverters(new[] { new DynamicJsonConverter() });

dynamic obj = serializer.Deserialize(json, typeof(object));

So, given a JSON string:

{
"Items":[
{ "Name":"Apple", "Price":12.3 },
{ "Name":"Grape", "Price":3.21 }
],
"Date":"21/11/2010"
}

The following code will work at runtime:

dynamic data = serializer.Deserialize(json, typeof(object));

data.Date; // "21/11/2010"
data.Items.Count; // 2
data.Items[0].Name; // "Apple"
data.Items[0].Price; // 12.3 (as a decimal)
data.Items[1].Name; // "Grape"
data.Items[1].Price; // 3.21 (as a decimal)

How to conditionally deserialize a json based on the property value

Any idea how this is possible?

This is not possible.

As @PalleDue said, you can do it post deserialization using .Where() clause

List<InboundJson> jsonobj = JsonConvert.DeserializeObject<List<InboundJson>>(result);

var result = jsonobj.Where(x => x.isActive);

Newtonsoft JSON auto map specific properties

You could create a custom generic JsonConverter that automatically maps from some model to DTO type during serialization, and maps back during deserialization, like so:

public class AutomapperConverter<TModel, TDTO> : JsonConverter
{
static readonly Lazy<MapperConfiguration> DefaultConfiguration
= new Lazy<MapperConfiguration>(() => new MapperConfiguration(cfg => cfg.CreateMap<TModel, TDTO>().ReverseMap()));

public AutomapperConverter(MapperConfiguration config) => this.config = config ?? throw new ArgumentNullException(nameof(config));
public AutomapperConverter() : this(DefaultConfiguration.Value) { }

readonly MapperConfiguration config;

public override bool CanConvert(Type type) => type == typeof(TModel);

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var dto = config.CreateMapper().Map<TDTO>(value);
serializer.Serialize(writer, dto);
}

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var dto = serializer.Deserialize<TDTO>(reader);
return config.CreateMapper().Map(dto, dto.GetType(), objectType);
}
}

And then add concrete instances to JsonSerializerSettings.Converters like so:

// Configure this statically on startup
MapperConfiguration configuration
= new MapperConfiguration(cfg =>
{
// Add all your mapping configurations here.
// ReverseMap() ensures you can map from and to the DTO
cfg.CreateMap<SomeNonSerializableType, SomeSerializableType>().ReverseMap();
});

// Set up settings using the global configuration.
var settings = new JsonSerializerSettings
{
Converters = { new AutomapperConverter<SomeNonSerializableType, SomeSerializableType>(configuration) },
};
var json = JsonConvert.SerializeObject(someClass, Formatting.Indented, settings);

var deserialized = JsonConvert.DeserializeObject<SomeClass>(json, settings);

Notes:

  • The converter is not intended to handle polymorphism.

  • The converter does not support preservation of object references, but could be extended to do so by using JsonSerializer.ReferenceResolver as shown in JsonConverter resolve reference or Custom object serialization vs PreserveReferencesHandling.

  • The AutoMapper docs suggest:

    Configuration should only happen once per AppDomain.

    However the converter will configure a default mapping if none is passed in.

Demo fiddle here.



Related Topics



Leave a reply



Submit