Search
Close this search box.

JSON.NET Custom Converters–A Quick Tour

Tuesday, July 26, 2011 1:22 PM

I have to admit that I’m a basic user when it comes to JSON serialization/deserialization.  I’ve used JSON.NET and the DataContractJsonSerializer.  I’ve read that JSON.NET is faster and more efficient than the built-in .NET serializer, but I haven’t had to build a system that is dependent on squeezing microseconds out of my serialization routines.  That said, I do prefer JSON.NET because it is more flexible when it comes to using DataContractAttribute and DataMemberAttribute for customizing your JSON output.

So I came across an interesting question on StackOverflow today, asking how a json string like:

1 {
  ‘one’ : 1, ‘two’ : 2, ‘three’ : 3, ‘four’ : 4, ‘blah’ : 100
}

would have its “one” and “two” properties deserialized to an object’s One and Two properties (easy) and anything else in the json string would be dumped into a Dictionary<string,object> (hmmm…not so easy).  So the resulting object would look like:

Mapped mappedObj = { One = 1;
Two = 2;
TheRest = [ { three = 3 }, { four = 4 }, { blah = 100 }]'; 
}

I’ve read about custom JSON.NET converters, but had never written one.  So I decided to give it a shot and discovered that it’s really not too bad.  Here’s my sample code:

using System;
using System.Collections.Generic;
using System.Linq;

using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using System.Reflection;

namespace JsonConverterTest1 {
  public class Mapped {
    private Dictionary<string, object> _theRest =
        new Dictionary<string, object>();
    public int One { get; set; }
    public int Two { get; set; }
    public Dictionary<string, object> TheRest {
      get { return _theRest; }
    }
  }

  public class MappedConverter : CustomCreationConverter<Mapped> {
    public override Mapped Create(Type objectType) {
      return new Mapped();
    }

    public override object ReadJson(JsonReader reader, Type objectType,
                                    object existingValue,
                                    JsonSerializer serializer) {
      var mappedObj = new Mapped();
      // get an array of the object's props so I can check if the JSON prop s/b
      // mapped to it
      var objProps =
          objectType.GetProperties().Select(p => p.Name.ToLower()).ToArray();

      // loop through my JSON string
      while (reader.Read()) {
        // if I'm at a property...
        if (reader.TokenType == JsonToken.PropertyName) {
          // convert the property to lower case
          string readerValue = reader.Value.ToString().ToLower();
          if (reader.Read())  // read in the prop value
          {
            // is this a mapped prop?
            if (objProps.Contains(readerValue)) {
              // get the property info and set the Mapped object's property
              // value
              PropertyInfo pi = mappedObj.GetType().GetProperty(
                  readerValue, BindingFlags.IgnoreCase | BindingFlags.Public |
                                   BindingFlags.Instance);
              var convertedValue =
                  Convert.ChangeType(reader.Value, pi.PropertyType);
              pi.SetValue(mappedObj, convertedValue, null);
            } else {
              // otherwise, stuff it into the Dictionary
              mappedObj.TheRest.Add(readerValue, reader.Value);
            }
          }
        }
      }
      return mappedObj;
    }
  }

  public class Program {
    static void Main(string[] args) {
      // a sample JSON string to deserialize
      string json = "{'one':1, 'two':2, 'three':3, 'four':4}";

      // call DeserializeObject, passing in my custom converter
      Mapped mappedObj =
          JsonConvert.DeserializeObject<Mapped>(json, new MappedConverter());

      // output some of the properties that were stuffed into the Dictionary
      Console.WriteLine(mappedObj.TheRest["three"].ToString());
      Console.WriteLine(mappedObj.TheRest["four"].ToString());
    }
  }
}

It’s pretty simple to create a custom converter and it’s almost limitless as to what you can do with it.

Of course, my sample code above is pretty simple and doesn’t take into account arrays or nested objects in the JSON string; but, that can be accounted for by using the JsonToken enumeration (which I do above in detecting a property) and checking for the start of a nested object or an array.

I found this an interesting exercise and gave me an opportunity to take a tour of a feature in JSON.NET that I’ve read about but never used.  I hope you find it interesting

This article is part of the GWB Archives. Original Author: david hoerster

Related Posts