One solution would be to create a custom JsonConverter
for Dictionary<string, object>
that adapts the logic from this answer to How do I use JSON.NET to deserialize into nested/recursive Dictionary and List? by Brian Rogers to return the specific types of arrays required, rather than just List<object>
collections.
First, define the following converter and extension methods:
public class ObjectDictionaryConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Dictionary<string, object>);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var tokenType = reader.SkipComments().TokenType;
if (tokenType == JsonToken.Null)
return null;
var tempDictionary = new Dictionary<string, object>();
var old = reader.DateParseHandling;
try
{
// Disable recognition of date strings
reader.DateParseHandling = DateParseHandling.None;
serializer.Populate(reader, tempDictionary);
}
finally
{
reader.DateParseHandling = old;
}
var dictionary = existingValue as IDictionary<string, object> ?? (IDictionary<string, object>)serializer.ContractResolver.ResolveContract(objectType).DefaultCreator();
foreach (var pair in tempDictionary)
dictionary.Add(pair.Key, pair.Value.ToObject());
return dictionary;
}
public override bool CanWrite { get { return false; } }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
public static partial class JsonExtensions
{
public static JsonReader SkipComments(this JsonReader reader)
{
while (reader.TokenType == JsonToken.Comment && reader.Read())
;
return reader;
}
public static object ToObject(this object obj)
{
return ToObject(obj as JToken) ?? obj;
}
public static object ToObject(this JToken token)
{
// Adapts the logic from https://stackoverflow.com/a/19140420/3744182)
// to https://stackoverflow.com/q/5546142/3744182
// By [Brian Rogers](https://stackoverflow.com/users/10263/brian-rogers)
if (token == null)
return null;
switch (token.Type)
{
case JTokenType.Null:
return null;
case JTokenType.Object:
return token.Children<JProperty>()
.ToDictionary(prop => prop.Name,
prop => ToObject(prop.Value));
case JTokenType.Array:
{
var list = token.Select(t => ToObject(t)).ToList();
if (list.All(i => i is long))
return list.Cast<long>().ToArray();
else if (list.All(i => i is string))
return list.Cast<string>().ToArray();
else return list.ToArray();
}
default:
return ((JValue)token).Value;
}
}
}
Then add the converter to JsonSerializerSettings.Converters
and deserialize as follows:
var settings = new JsonSerializerSettings
{
Converters = { new ObjectDictionaryConverter() },
};
var deserializedObj = JsonConvert.DeserializeObject<AttributeObject>(jsonString, settings);
Notes:
- Since you know that your dictionary values should be strings rather than
DateTime
values, I disabled Json.NET's automatic date recognition. For details about date recognition see Serializing Dates in JSON.
Demo fiddle here.