40

I want to have a property with type name in JSON when I serialize objects of certain types. I wrote a converter:

public class TypeInfoConverter : JsonConverter {
    private readonly IEnumerable<Type> _types;

    public TypeInfoConverter(IEnumerable<Type> types) {
        Contract.Requires(types != null);

        _types = types;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
        var jObject = JObject.FromObject(value, serializer);
        jObject.AddFirst(new JProperty("Type", value.GetType().Name));
        jObject.WriteTo(writer);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
        return serializer.Deserialize(reader, objectType);
    }

    public override bool CanConvert(Type objectType) {
        return _types.Any(t => t.IsAssignableFrom(objectType));
    }
}

But when I'm trying to serialize object I have an infinity recursion here: var jObject = JObject.FromObject(value, serializer); It is obvious because I use the same instance of JsonSerializer which was configured with that converter.

How to prevent using this converter, but I want to use other converters which configured for this serializer?

Types which I want to serialize:

public interface ITaskResult {
}

public class UserHasRejectedOffer : ITaskResult {
    public string Message { get; set; }
}

public class UserHasFilledForm : ITaskResult {
    public string FormValue1 { get; set; }

    public string Formvalue2 { get; set; }
}

...
1
  • Post the code of types you are trying to serialize. Commented Oct 14, 2013 at 17:33

4 Answers 4

78
var jsonSerializerSettings = new JsonSerializerSettings() { 
    TypeNameHandling = TypeNameHandling.All
};
var json = JsonConvert.SerializeObject(instance, jsonSerializerSettings);

http://james.newtonking.com/json/help/index.html?topic=html/SerializationSettings.htm

Sign up to request clarification or add additional context in comments.

1 Comment

Very simple solution. Any way to just output the class name without the namespace and project name?
16
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
    var converters = serializer.Converters.Where(x => !(x is TypeInfoConverter)).ToArray();

    var jObject = JObject.FromObject(value);
    jObject.AddFirst(new JProperty("Type", value.GetType().Name));
    jObject.WriteTo(writer, converters);
}

1 Comment

This is a better solution than mine. You can actually just do jObject.WriteTo(writer, converters) like you were doing originally instead of writing each property individually.
3

@CalvinDale shows how to serialize object, but the main chalenge is how to deserialize back an object with type info. This is an example how to do this. For this example we have an array of 3 different classes objects that contain the same interface

IAnimal[] animals = new IAnimal[] {
                    new Cat{CatName="Tom"},
                    new Dog{DogName="Scoopy"},
                    new Rabbit{RabitName="Honey"}
    };

classes

public class Cat : IAnimal { public string CatName { get; set; } }
public class Dog : IAnimal { public string DogName { get; set; } }
public class Rabbit : IAnimal { public string RabitName { get; set; } }

public interface IAnimal { }

Code to serialize

    var jsonSerializerSettings = new JsonSerializerSettings()
    {
        TypeNameHandling = TypeNameHandling.All
    };
    var json = JsonConvert.SerializeObject(animals, jsonSerializerSettings);

here is the most difficult part - how to deserialize back

List<IAnimal> animalsBack = ((JArray)JsonConvert.DeserializeObject(json))
.Select(o => (IAnimal)JsonConvert.DeserializeObject(o.ToString(), 
Type.GetType((string)o["$type"]))).ToList();

Test

json = JsonConvert.SerializeObject(animalsBack, Newtonsoft.Json.Formatting.Indented);

test result

[
  {
    "CatName": "Tom"
  },
  {
    "DogName": "Scoopy"
  },
  {
    "RabitName": "Honey"
  }
]

Comments

2

Have you tried creating a new instance of JsonSerializer, then copying all the converters from the original serializer except the converter that causes the infinite recursion?

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
    JsonSerializerSettings settings = new JsonSerializerSettings
    {
        Converters = serializer.Converters.Where(s => !(s is TypeInfoConverter)).ToList()
        // also copy any other custom settings from the serializer you wish to pass through
        DateFormatHandling = serializer.DateFormatHandling,
        MissingMemberHandling = serializer.MissingMemberHandling,
        NullValueHandling = serializer.NullValueHandling,
        Formatting = serializer.Formatting
    };
    var localSerializer = JsonSerializer.Create(settings);

    var jObject = JObject.FromObject(value, localSerializer);
    jObject.AddFirst(new JProperty("Type", value.GetType().Name));
    jObject.WriteTo(writer);
}

6 Comments

But I still need other JsonSerializerSettings like DateFormatHandling, MissingMemberHandling etc.
Yes, I noted that in the code comments above. Just copy the settings you need from the outer serializer to the new one.
It does not work for me. When executing var jObject = JObject.FromObject(value, localSerializer); it tries to serialize using this converter. Maybe it's because of the attribute in the Base class? [JsonConverter(typeof(InputConverter))] public abstract class Input { I'm missing something here :-(
@ferarias If you have a [JsonConverter] attribute somewhere, it will always be used regardless of the list of converters on a particular serializer. The only way to override it then is to use a custom ContractResolver.
Ok, and it's not possible to "disable" it somehow, is it?
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.