I'm trying to deserialize some JSON:
{
"a":1,
"b":25,
"c":"1-7",
"obj1":{
"a1":10,
"b1":45,
"c1":60
},
"obj2":[
{
"a2":100,
"b2":15,
"c2":50
},
{
"e2":"1,2,5-7",
"f2":"1,3-5",
"a2":25
}
]
}
I want to find a way to define a custom de-serialization only for some fields.
In the following code, I separated the fields that need some attention (custom processing) and the ones that could be done automatically somehow.
Is it possible to automatically deserialize the "normal" fields? (that don't need any specific custom processing)
[JsonConverter(typeof(ConfigurationSerializer))]
public class Configuration
{
public int a { get; set; }
public int b { get; set; }
public Obj1 obj1 { get; set; }
public int[] c { get; set; }
public IList<Obj2> obj2 { get; set; }
}
public class ConfigurationSerializer : 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)
{
JObject jsonObject = JObject.Load(reader);
Configuration configuration = new Configuration();
// I would like this part to be automatic as I just repeat the default
// In the real case, I have many fields here!
configuration.a = (int)jsonObject["a"];
configuration.b = (int)jsonObject["b"];
configuration.obj1 = jsonObject["obj1"].ToObject<Obj1>();
// I created the JsonConverter for those 2 properties
configuration.c = myCustomProcessMethod(jsonObject["c"]);
configuration.obj2 = myCustomProcessMethod2(jsonObject["obj2"].ToObject<ValletConfiguration>());
return configuration;
}
public override bool CanConvert(Type objectType)
{
return typeof(Configuration).IsAssignableFrom(objectType);
}
}
Since you are annotating your type with Json.NET attributes anyway, a simpler solution would seem to be to put the converters on the relevant properties using [JsonConverter(Type)]
or [JsonProperty(ItemConverterType = Type)]
:
public class Configuration
{
public int a { get; set; }
public int b { get; set; }
public Obj1 obj1 { get; set; }
// Converts the entire list to a compressed string
[JsonConverter(typeof(IntListConverter))]
public int[] c { get; set; }
// Converts each Obj2 item individually
[JsonProperty(ItemConverterType = typeof(Obj2Converter))]
public IList<Obj2> obj2 { get; set; }
}
Nevertheless, if you need to retain the converter on Configuration
(or are actually adding the converter to JsonSerializerSettings.Converters
and cannot add Json.NET attributes to your type), you can use JsonSerializer.Populate()
to populate the standard properties, as long as you first remove the custom properties from the JObject
:
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
var jsonObject = JObject.Load(reader);
var configuration = (existingValue as Configuration ?? new Configuration());
// I created the JsonConverter for those 2 properties
configuration.c = myCustomProcessMethod(jsonObject["c"].RemoveFromLowestPossibleParent());
configuration.obj2 = myCustomProcessMethod2(jsonObject["obj2"].RemoveFromLowestPossibleParent().ToObject<ValletConfiguration>());
// Populate the remaining standard properties
using (var subReader = jsonObject.CreateReader())
{
serializer.Populate(subReader, configuration);
}
return configuration;
}
Using the extension method:
public static class JsonExtensions
{
public static JToken RemoveFromLowestPossibleParent(this JToken node)
{
if (node == null)
return null;
var contained = node.AncestorsAndSelf().Where(t => t.Parent is JContainer && t.Parent.Type != JTokenType.Property).FirstOrDefault();
if (contained != null)
contained.Remove();
// Also detach the node from its immediate containing property -- Remove() does not do this even though it seems like it should
if (node.Parent is JProperty)
((JProperty)node.Parent).Value = null;
return node;
}
}