38

I have a JsonConverter that, depending on an instance specific flag, needs to either

  • run custom serialization logic
  • run the default Json.NET serialization logic

How can the default Json.NET serialization logic be ran from a JsonConverter?

Thanks

sient
  • 600
  • 1
  • 5
  • 11
  • possible duplicate of [Recursively call JsonSerializer in a JsonConverter](http://stackoverflow.com/questions/16085805/recursively-call-jsonserializer-in-a-jsonconverter) – David Pfeffer Feb 02 '15 at 19:34

2 Answers2

15

Here is an example. Say your class to serialize looks like this:

class Foo
{
    public bool IsSpecial { get; set; }
    public string A { get; set; }
    public string B { get; set; }
    public string C { get; set; }
}

The IsSpecial flag is used to control whether we do something special in the converter or just let things serialize naturally. You can write your converter like this:

class FooConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return typeof(Foo).IsAssignableFrom(objectType);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        Foo foo = (Foo)value;
        JObject jo;
        if (foo.IsSpecial)
        {
            // special serialization logic based on instance-specific flag
            jo = new JObject();
            jo.Add("names", string.Join(", ", new string[] { foo.A, foo.B, foo.C }));
        }
        else
        {
            // normal serialization
            jo = JObject.FromObject(foo);
        }
        jo.WriteTo(writer);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

Then, to use the converter, pass an instance of it to the SerializeObject method (e.g. in the settings). (Do NOT decorate the target class with a JsonConverter attribute, or this will result in an infinite recursive loop when you serialize.)

class Program
{
    static void Main(string[] args)
    {
        List<Foo> foos = new List<Foo>
        {
            new Foo
            {
                A = "Moe",
                B = "Larry",
                C = "Curly",
                IsSpecial = false
            },
            new Foo
            {
                A = "Huey",
                B = "Dewey",
                C = "Louie",
                IsSpecial = true
            },
        };

        JsonSerializerSettings settings = new JsonSerializerSettings();
        settings.Converters.Add(new FooConverter());
        settings.Formatting = Formatting.Indented;

        string json = JsonConvert.SerializeObject(foos, settings);
        Console.WriteLine(json);
    }
}

Output:

[
  {
    "IsSpecial": false,
    "A": "Moe",
    "B": "Larry",
    "C": "Curly"
  },
  {
    "names": "Huey, Dewey, Louie"
  }
]
Brian Rogers
  • 125,747
  • 31
  • 299
  • 300
  • 3
    This works well, except that I have custom converters that need to be included in the "normal" serialization process. So for this example, `FooConverter` also needs to be included in the serialization process. This then causes the "normal" serialization to go through the `FooConverter` again, with `IsSpecial = false`. Stated differently, the problem was to serialize a field encountered during serialization differently than an object passed directly into `JsonConvert.Serialize`. A (non-optimal) solution is to require that all field references be annotated with a `[JsonConverter]` reference. – sient Jan 27 '14 at 17:41
  • I think I may have misunderstood your question then. Can you edit your question to include a simple example (Foo, Bar, etc.) of the class hierarchy you are trying to serialize and what your desired JSON output would be if your converter worked the way you wanted? It sounds like you want it to behave differently depending on whether the object to be serialized is at the root level or not, as opposed to being controlled by a flag on the object instances. – Brian Rogers Jan 27 '14 at 18:51
  • 2
    This works terrifically as long as you are not trying to preserve references. It does not appear to work if you need the id field, however. – William Jockusch Jun 11 '16 at 12:26
  • Custom converters, or custom serializer settings can be accomplished by passing in a new `JsonSerializer` into `JObject.FromObject`. For example: `var jo = JObject.FromObject(value, JsonSerializer.CreateDefault(new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All }));` – brthornbury Oct 18 '18 at 04:51
7

You can change the CanWrite property to disable a custom serializer. This won't work right if the object can contain children of the same type or if you are serializing in more than one thread.

class FooConverter : JsonConverter
{
    bool _canWrite = true;
    public override bool CanWrite
    {
        get { return _canWrite;}
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        Foo foo = (Foo)value;
        JObject jo;
        if (foo.IsSpecial)
        {
            // special serialization logic based on instance-specific flag
            jo = new JObject();
            jo.Add("names", string.Join(", ", new string[] { foo.A, foo.B, foo.C }));
        }
        else
        {
            // normal serialization
            _canWrite = false;
            jo = JObject.FromObject(foo);
            _canWrite = true;
        }
        jo.WriteTo(writer);
    }
}
piojo
  • 6,351
  • 1
  • 26
  • 36
agoaj
  • 369
  • 6
  • 13
  • 1
    +1 -- I have a custom contract resolver that creates a hidden json converter -- each one is it's own instance, so modifying the instance level `CanRead` and `CanWrite` to prevent recursion is working perfectly for me. -- Though, I'm using `serializer.Serialize(writer, value);` to write the value, and I recommend setting the value back to the original value in a `finally` block so that it never gets stuck in a weird state. – BrainSlugs83 Oct 12 '18 at 23:39