Chris Gessler
Chris Gessler

Reputation: 23123

Deserializing name / value pairs to objects

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

Answers (1)

Brian Rogers
Brian Rogers

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

Related Questions