C# - Newtonsoft.Json

Created:2019-07-17  Last modified:2019-07-17


  1. Introduction

    Attribute & inheritance, missing fields. two way conversion

    Newtonsoft.Json is C# JSON serializer and deserializer library for .NET platform (including .NET Framework and .NET Core). Inside the implementation, it provides two layers of json handling class.
    At the top: JsonCovert is a simple wrapper that allows to convert JSON from object and string
    At the bottom: JsonSerializer allows to read/write json from/to stream, setup formatter converters, e.g. JavaScriptDateTimeConverter()

    Serialization: from an object to a string, Deserialization: from a string to an object

    1. Handler

      Handlers are used when encountering different conditions, e.g. 1). a field that is not defined in object but is given in json string, 2). how to handle date string.

    2. Default converting

      C#JSON
      string, Type, Guidstring
      Byte[]string (base64 encoded)
      DateTimestring
      byte, int, float...number
      Enumnumber
      boolboolean
      IEnumerable (list, collection, array)array
      Dictionary, objectobject
    3. Serialization (from object to json)

      1). Which field/Property should be serialized? Controlled by JsonObjectAttribute

          public enum MemberSerialization{
              // All public fields/properties are serialized. Members can be excluded using JsonIgnoreAttribute
              OptOut = 0, // default
              // All public fields/properties marked with JsonPropertyAttribute are serialized
              OptIn = 1,
              // all public, private fields are serialized. Properties are also serialized with weired names.
              Fields = 2
          }
          [JsonObject(MemberSerialization.OptOut)]
          public class Person{ /*...*/}
                      

      2). How to control the name of each field/property on json? The name is first controlled by JsonProperty, and then by the field/property name itself.

      JsonProperty indicates
      1). this field/properties should be include in JSON even if it isn't public,
      2). with assigning an optional name,
      3). with a customized JsonConverter,
      4). order in json

                      public class Person{
                          public string LastName {get; set;}
                          [JsonProperty("firstname", Order = 1)]
                          public string FirstName;
                      }
                  
                      {
                          "LastName":"Qin",
                          "firstname": "Nan"
                      }
                  

      3). Default value handling: when serializing, an object's field/property can be/not be ignored if it's value is the type's default value (e.g. null, 0). When deserializing set members to their default value if they don't exist in json.

                      public class Person{
                          [JsonProperty("age", DefaultValueHandling = DefaultValueHandling.Ignore)] // ignore this field in json if it is 0.
                          private int age = 0;
                      }
                  

      4). For complex inner field/property, we can define a customized json converter to handle it.

                      public class Person{
                          [JsonConverter("job", typeof(JobJsonConverter))]
                          public Job Job { get; set; }
                      }
                      public class Job{
                          public string Location;
                          public string Description;
                      }
                  
    4. Deserialization (from json to object of a specific type)

    5. Customized JsonConverter

      Even though Newtonsoft.json can also be used to handle complex class, but sometime customized converter is still desired. A convert is a 2-way converting, and it needs to override CanRead & CanWrite to indicate which direction it supports. A convert will inherit JsonConverter

          // json --- object
          public abstract class JsonConverter{
              protected JsonConverter();
              public virtual bool CanRead { get; } // from json to object
              public virtual bool CanWrite { get; } // from object to json
              public abstract bool CanConvert(Type objectType);
              public abstract object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer);
              public abstract void WriteJson(JsonWriter writer, object value, JsonSerializer serializer);
          }
          // json --- object of a specific type
          public abstract class JsonConverter<T> : JsonConverter{
              // read data from reader, and convert and return a T object 
              public abstract T ReadJson(JsonReader reader, Type objectType, T existingValue, bool hasExistingValue, JsonSerializer serializer);
              // convert value to json and write into writer.
              public abstract void WriteJson(JsonWriter writer, T value, JsonSerializer serializer);
          }
      
                  

      JsonReader and JsonWriter are abstract classes that handling json I/O. For example, JObject.Load(reader) read the data out and return a JObject object. writer.WriteValue("1") write data.

      Convert HttpRequest to a JObject

                  // HttpResponseMessage response = 
                  var content = response.Content;
                  using (var contentStream = await content.ReadAsStreamAsync())
                  using (var streamReader = new StreamReader(contentStream))
                  using (var jsonReader = new JsonTextReader(streamReader))
                  {
                      return JObject.Load(jsonReader);
                  }
                  
    6. JObject

      JObject

  2. Examples

    1. Serialize C# Object to JSON string

  3. References