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
HTML Agility Pack - Removing Unwanted Tags Without Removing Content
ASP.NET Core 2.0 Disable Automatic Challenge
Asynchronous Iterator Task<Ienumerable<T>>
Programmatic Way to Get All the Available Languages (In Satellite Assemblies)
Reasons for Why a Winforms Label Does Not Want to Be Transparent
Best Practice: Convert Linq Query Result to a Datatable Without Looping
How to Detect the Original MAC Address After It Has Been Spoofed
How to Copy Value from Class X to Class Y with the Same Property Name in C#
How to Run Commands on Ssh Server in C#
Unlock Windows Programmatically
Creating an Anonymous Type Dynamically
Lambda Expressions in Immediate Window for VS2015