There are a few things going on here. First, a preliminary issue: even with no TypeConverter
applied, your JSON does not correspond to your class Foo
, it corresponds to some container class that contains a Foo
property, for instance:
public class TestClass
{
public Foo Foo { get; set; }
}
I.e. given your JSON string, the following will not work:
var json = "{\"Foo\":{\"a\":true,\"b\":false,\"c\":false}}";
var foo = JsonConvert.DeserializeObject<Foo>(json);
But the following will:
var test = JsonConvert.DeserializeObject<TestClass>(json);
I suspect this is simply a mistake in the question, so I’ll assume you are looking to deserialize a class contain a property Foo
.
The main problem you are seeing is that Json.NET will try to use a TypeConverter
if one is present to convert a class to be serialized to a JSON string. From the docs:
Primitive Types
.Net:
TypeConverter
(convertible to String)
JSON: String
But in your JSON, Foo
is not a JSON string, it is a JSON object, thus deserialization fails once the type converter is applied. An embedded string would look like this:
{"Foo":"{\"a\":true,\"b\":false,\"c\":false}"}
Notice how all the quotes have been escaped. And even if you changed your JSON format for Foo
objects to match this, your deserialization would still fail as the TypeConverter
and Json.NET try to call each other recursively.
Thus what you need to do is to globally disable use of the TypeConverter
by Json.NET and fall back to default serialization while retaining use of the TypeConverter
in all other situations. This is a bit tricky since there is no Json.NET attribute you can apply to disable use of type converters, instead you need a special contract resolver plus a special JsonConverter
to make use of it:
public class NoTypeConverterJsonConverter<T> : JsonConverter
{
static readonly IContractResolver resolver = new NoTypeConverterContractResolver();
class NoTypeConverterContractResolver : DefaultContractResolver
{
protected override JsonContract CreateContract(Type objectType)
{
if (typeof(T).IsAssignableFrom(objectType))
{
var contract = this.CreateObjectContract(objectType);
contract.Converter = null; // Also null out the converter to prevent infinite recursion.
return contract;
}
return base.CreateContract(objectType);
}
}
public override bool CanConvert(Type objectType)
{
return typeof(T).IsAssignableFrom(objectType);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return JsonSerializer.CreateDefault(new JsonSerializerSettings { ContractResolver = resolver }).Deserialize(reader, objectType);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
JsonSerializer.CreateDefault(new JsonSerializerSettings { ContractResolver = resolver }).Serialize(writer, value);
}
}
And use it like:
[TypeConverter(typeof(FooConverter))]
[JsonConverter(typeof(NoTypeConverterJsonConverter<Foo>))]
public class Foo
{
public bool a { get; set; }
public bool b { get; set; }
public bool c { get; set; }
public Foo() { }
}
public class FooConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, System.Type sourceType)
{
if (sourceType == typeof(string))
{
return true;
}
return base.CanConvertFrom(context, sourceType);
}
public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
{
if (value is string)
{
string s = value.ToString();
//s = s.Replace("\\", "");
Foo f = JsonConvert.DeserializeObject<Foo>(s);
return f;
}
return base.ConvertFrom(context, culture, value);
}
}
Example fiddle.
Finally, you should probably also implement the ConvertTo
method in your type converter, see How to: Implement a Type Converter.