I have an application that gets data from multiple API's.
To minimize the amount of classes, I would need to map to every property.
I have implemented a simple json.net ContractResolver.
However, when I try to map a property to a child property I run into some trouble.
JSON format 1:
{
"event_id": 123,
"event_name": "event1",
"start_date": "2018-11-30",
"end_date": "2018-12-04",
"participants": {
"guests": [
{
"guest_id": 143,
"first_name": "John",
"last_name": "Smith",
},
{
"guest_id": 189,
"first_name": "Bob",
"last_name": "Duke",
}
]
}
}
JSON format 2:
{
"name": "event2",
"from": "2017-05-05",
"to": "2017-05-09",
"city":"Some other city",
"country":"US",
"guests": [
{
"email":"[email protected]",
"firstName":"Jane",
"lastName":"Smith",
"telephone":"1-369-81891"
}
],
}
Here are my model classes:
public class Event
{
public int EventId { get; set; }
public string EventName { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public List<Guest> Guests { get; set; }
}
public class Guest
{
public string GuestId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
}
And my resolver:
public class EventResolver : DefaultContractResolver
{
private Dictionary<string,string> PropertyMappings { get; set; }
public EventResolver()
{
this.PropertyMappings = new Dictionary<string, string>
{
{"EventId", "event_id"},
{"StartDate", "start_date" },
{"EndDate", "end_date" },
{"EventName", "event_name" },
{"Guests", "participants.guests"}
};
}
protected override JsonContract CreateContract(Type objectType)
{
return base.CreateContract(objectType);
}
protected override string ResolvePropertyName(string propertyName)
{
var resolved = this.PropertyMappings.TryGetValue(propertyName, out var resolvedName);
return (resolved) ? resolvedName : base.ResolvePropertyName(propertyName);
}
}
I understand a path won't work instead of a property name. How could one go about this?
I don't think the resolver idea is going to work because you are remapping more than just property names -- you are also trying to deserialize into a class structure that doesn't always match the shape of JSON. This job is better suited for a set of
JsonConverters.Here's the basic approach:
JsonConverterfor each model class for which the JSON varies.ReadJsonmethod load aJObjectfrom the reader.event_idalways being present in the first format, that is a good way to detect it, because you know the second format does not have that property. You can base this check on the presence of multiple properties if needed; the key is just to use some combination that appears in only one format and no others. (Or if you know ahead of time which format to expect, you can simply parameterize the converters, i.e. pass a format flag in the constructor.)JObject.For the
Eventmodel shown in your question, the converter might look something like this:Similarly for the
Guestmodel, we might have thisJsonConverter:To use the converters, add them to the
Converterscollection of theJsonSerializerSettingsobject and pass the settings toDeserializeObject()like this:Demo fiddle: https://dotnetfiddle.net/KI82KB