Reputation: 23123
I have a collection of name / value pairs where they are defined with the words name and value just like a Key/Value object, i.e.
[{"Name":"ActivityId","DataType":1,"Value":"a7868f8c-07ac-488d-a414-714527c2e76f"},
{"Name":"Address1","DataType":2,"Value":"123 Main St"}]
If I had an object like:
class Request
{
public Guid ActivityId { get; set; }
public string Address1 {get; set; }
}
How can I deserialize this to the class above?
Should I consider a custom converter? Does Json.NET have something built-in? Is there a way to decorate the properties with an attribute that I'm missing? Would it be easier to customize the serialization?
I'm trying to avoid pulling the data for each property from a Dictionary, which would be the easy route, but would require me to do this with each custom implementation. I would prefer to do this in a base class in a single method using Json.NET (or something in the .NET framework).
I've searched quite a bit, and most examples are real name/value pairs, not prefixed with name and value, i.e.
[{"ActivityId":"a7868f8c-07ac-488d-a414-714527c2e76f"}]
Any ideas?
Upvotes: 5
Views: 1612
Reputation: 129827
This can be done in a straightforward manner with a custom JsonConverter
like the one below. The converter works by first transforming the array of name-value pairs into a JObject
with properties mirroring the pairs, then populating the target object from the JObject
using the serializer's built-in Populate
method.
class NameValueConverter : JsonConverter
{
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
// Load the array of name-value pairs and transform into a JObject.
// We are assuming all the names will be distinct.
JObject obj = new JObject(
JArray.Load(reader)
.Children<JObject>()
.Select(jo => new JProperty((string)jo["Name"], jo["Value"]))
);
// Instantiate the target object and populate it from the JObject.
object result = Activator.CreateInstance(objectType);
serializer.Populate(obj.CreateReader(), result);
return result;
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
// WriteJson is not called when CanWrite returns false
throw new NotImplementedException();
}
public override bool CanConvert(Type objectType)
{
// We only want this converter to handle classes that are expressly
// marked with a [JsonConverter] attribute, so return false here.
// (CanConvert is not called when [JsonConverter] attribute is used.)
return false;
}
}
To use the converter, just add a [JsonConverter]
attribute to the target class:
[JsonConverter(typeof(NameValueConverter))]
class Request
{
public Guid ActivityId { get; set; }
public string Address1 {get; set; }
}
Then, you can deserialize as you normally would:
Request req = JsonConvert.DeserializeObject<Request>(json);
Fiddle: https://dotnetfiddle.net/tAp1Py
Upvotes: 5