Newtonsoft.JSON无法使用TypeConverter属性转换模型

我有一个C#MVC应用程序,它将数据作为JSON字符串存储在XML文档中,也存储在MySQL数据库表中。

最近我收到了在MySQL数据库字段中存储JSON字符串的要求,要通过Newtonsoft.Json转换为C#对象 ,所以我决定实现一个TypeConverter将JSON字符串转换为自定义C#模型。

不幸的是,当TypeConverter属性添加到我的C#Model时,我无法在我的解决方案中的任何地方使用以下命令来反序列化我的JSON字符串:

JsonConvert.DeserializeObject(json); 

删除属性可以解决问题但是这会阻止我将MySQL DB字段转换为自定义C#对象。

这是我添加了TypeConverter属性的C#模型

 using System.ComponentModel; [TypeConverter(typeof(FooConverter))] public class Foo { public bool a { get; set; } public bool b { get; set; } public bool c { get; set; } public Foo(){} } 

这是我的TypeConverter类

 using Newtonsoft.Json; using System; using System.ComponentModel; 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().Replace("\\",""); Foo f = JsonConvert.DeserializeObject(s); return f; } return base.ConvertFrom(context, culture, value); } } } 

一旦我将属性添加到Foo类,我收到以下错误:

无法将当前JSON对象(例如{“name”:“value”})反序列化为类型“Models.Foo”,因为该类型需要JSON字符串值才能正确反序列化。

要修复此错误,请将JSON更改为JSON字符串值或更改反序列化类型,使其成为正常的.NET类型(例如,不是像整数这样的基本类型,而不是像数组或列表那样的集合类型),可以反序列化来自JSON对象。 JsonObjectAttribute也可以添加到类型中以强制它从JSON对象反序列化。

我使用以下字符串(在没有添加TypeConverter属性的情况下完美运行):

 "{\"Foo\":{\"a\":true,\"b\":false,\"c\":false}}" 

不知道这里发生了什么,有什么想法吗?

非常感谢!!!

UPDATE

我发现我在MVC API控制器上的操作也存在问题,这些操作接受带有Foo作为属性Test Class,或者在将TypeConverter属性添加到Foo类时接受Foo作为对象的控制器上。

以下是具有问题的测试控制器的示例:

 public class TestController : ApiController { [AcceptVerbs("POST", "GET")] public void PostTestClass(TestClass t) { // Returns null when TypeConverter attribute is added to the Foo Class return t.Foo; } AcceptVerbs("POST", "GET")] public void PostFooObj(Foo f) { // Returns null when TypeConverter attribute is added to the Foo Class return f; } } 

TypeConverter可能导致覆盖WebAPI模型绑定的问题,并且当上述任一操作通过AJAX通过以下结构接收JSON时返回null:

 // eg. PostTestClass(TestClass T) {'Foo': {'a': false,'b': true,'c': false}}; // eg. PostFooObj(Foo f) {'a': false,'b': true,'c': false} 

将TypeConverter属性添加到Foo类时,只要找到路径,就会调用FooConverter TypeConverter类上的以下方法:

  public override bool CanConvertFrom(ITypeDescriptorContext context, System.Type sourceType) { if (sourceType == typeof(string)) { return true; } return base.CanConvertFrom(context, sourceType); } 

FooConverter TypeController上的ConvertFrom方法不是由ApiController的动作调用的,这可能是问题的原因。

同样,这是类似的情况,其中控制器操作将在没有TypeConverter属性的情况下正常工作。

非常感谢!!

非常感谢。

这里有一些事情发生。 首先,一个初步问题:即使没有应用TypeConverter ,你的JSON也不对应你的类Foo ,它对应于一些包含Foo属性的容器类,例如:

 public class TestClass { public Foo Foo { get; set; } } 

即给出您的JSON字符串,以下将不起作用:

 var json = "{\"Foo\":{\"a\":true,\"b\":false,\"c\":false}}"; var foo = JsonConvert.DeserializeObject(json); 

但以下将:

 var test = JsonConvert.DeserializeObject(json); 

我怀疑这只是问题中的一个错误,所以我假设您正在寻找反序列化包含属性Foo的类。

您看到的主要问题是Json.NET 将尝试使用TypeConverter如果存在)将要序列化的类转换为JSON字符串 。 来自文档 :

原始类型

.Net: TypeConverter (可转换为String)
JSON:字符串

但是在你的JSON中, Foo不是JSON字符串,它是一个JSON 对象 ,因此一旦应用了类型转换器,反序列化就会失败。 嵌入的字符串如下所示:

 {"Foo":"{\"a\":true,\"b\":false,\"c\":false}"} 

注意所有报价是如何被转义的。 即使你改变了Foo对象的JSON格式以匹配它,你的反序列化仍然会失败,因为TypeConverter和Json.NET试图以递归方式相互调用。

因此,您需要做的是全局禁用Json.NET对TypeConverter的使用,并回退到默认序列化,同时在所有其他情况下保留TypeConverter使用。 这有点棘手,因为没有Json.NET属性可以应用于禁用类型转换器,而是需要一个特殊的合约解析器和一个特殊的JsonConverter来使用它:

 public class NoTypeConverterJsonConverter : 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); } } 

并使用它像:

 [TypeConverter(typeof(FooConverter))] [JsonConverter(typeof(NoTypeConverterJsonConverter))] 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(s); return f; } return base.ConvertFrom(context, culture, value); } } 

示例小提琴 。

最后,您可能还应该在类型转换器中实现ConvertTo方法,请参见如何:实现类型转换器 。

避免此行为的简单方法是从转换检查中删除OR,即删除|| destinationType == typeof(string)

下面的一个例子..

  public class DepartmentBindModelConverter : TypeConverter { public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) { return destinationType == typeof(DepartmentViewModel); // Removed || destinationType == typeof(string), to allow newtonsoft json convert model with typeconverter attribute } public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { if (value == null) return null; if (destinationType == typeof(DepartmentViewModel) && value is DepartmentBindModel) { var department = (DepartmentBindModel) value; return new DepartmentViewModel { Id = department.Id, Name = department.Name, GroupName = department.GroupName, ReturnUrl = department.ReturnUrl }; } return base.ConvertTo(context, culture, value, destinationType); } } } 

如果你有一个结构而不是一个类,那么当尝试(de)序列化Nullable时,接受的答案仍会进入无限递归。

要避免修改CreateContract,如下所示:

  protected override JsonContract CreateContract(Type objectType) { if (typeof(T).IsAssignableFrom(objectType) || Nullable.GetUnderlyingType(objectType) != null && typeof(T).IsAssignableFrom(Nullable.GetUnderlyingType(objectType))) { var contract = this.CreateObjectContract(objectType); contract.Converter = null; // Also null out the converter to prevent infinite recursion. return contract; } return base.CreateContract(objectType); }