Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# JSON deserialization: can I intercept the deserialization and optionally change the result?

Tags:

json

c#

json.net

We've got some JSON that we are deserializing into a strongly-typed object graph in C#. However, we've got one issue: sometimes there is an "empty" value in the JSON (e.g., empty string) in a property that maps to a boolean value in our model.

In our case, we know that 100% of the time, we can translate these "blank" values to Boolean false.

However, the JSON deserializers I've tried don't know about this (understandably).

I've been trying to find a way to intercept the deserialization of each property, and optionally override the output. I.e., if there was an "interceptor" method I could register, that looked like this:

    public Boolean JsonDeserializationInterceptor(String rawJson, System.Type targetType, out object result)
    {
        Boolean overrodeValue = false;
        result = null;
        if(targetType == typeof(System.Boolean))
        {
            overrodeValue = true;
            // We'll pretend that we're interpreting the string "Yes" as
            // true, and all other values are false.
            if (rawJson != null && rawJson.ToLower() == "\"yes\"")
                result = true;
            else
                result = false;
        }

        return overrodeValue;
    }

That's just hypothetical, of course, but hopefully it gives you an idea of what I'm trying to accomplish.

In my research I have not been able to figure out a way to do this. I've looked at Json.NET, System.Runtime.Serialization.Json.DataContractJsonSerializer, and System.Web.Script.Serialization.JavaScriptSerializer. I bet there is a way to do this, and I just haven't been able to figure it out.

Edit: I think you might be able to use the JsonConverterAttribute, but so far I have not been able to get that working.

like image 658
Josh Avatar asked Jun 06 '13 21:06

Josh


2 Answers

Writing a custom JsonConverter for a primitive type is pretty straghtforward:

using System;
using Newtonsoft.Json;

namespace So16972364JsonDeserializeConverter
{
    class BoolConverter : JsonConverter
    {
        public override void WriteJson (JsonWriter writer, object value, JsonSerializer serializer)
        {
            serializer.Serialize(writer, value);
        }

        public override object ReadJson (JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            switch (reader.TokenType) {
                case JsonToken.String:
                    if ((string)reader.Value == "")
                        return false;
                    break;
                case JsonToken.Boolean:
                    return reader.Value;
            }
            throw new JsonReaderException("Expected boolean or empty string.");
        }

        public override bool CanConvert (Type objectType)
        {
            return objectType == typeof(bool);
        }
    }

    class Program
    {
        const string json = @"
{
    b1: true,
    b2: false,
    b3: ''
}
";

        static void Main ()
        {
            Foo foo = JsonConvert.DeserializeObject<Foo>(json, new JsonSerializerSettings {
                Converters = { new BoolConverter() }
            });
            Console.WriteLine(JsonConvert.SerializeObject(foo, Formatting.Indented));
            Console.ReadKey();
        }
    }

    class Foo
    {
        public bool b1, b2, b3;
    }
}

This code overrides deserialization of all boolean values. If you need this only for specific members, you need to apply JsonConverterAttribute.

like image 158
Athari Avatar answered Nov 11 '22 17:11

Athari


There is an error in the example above. Instead of

public override void WriteJson (JsonWriter writer, object value, JsonSerializer serializer)
{
    serializer.Serialize(writer, value);
}

there should be

public override void WriteJson (JsonWriter writer, object value, JsonSerializer serializer)
{
    writer.WriteValue(value);
}

otherwise you will get StackOverflow exception! Strangely why nobody hasn't written about it yet.

like image 22
Mykola Klymyuk Avatar answered Nov 11 '22 16:11

Mykola Klymyuk