Newtonsoft.JSON不能转换带有TypeConverter属性的模型
本文关键字:TypeConverter 属性 模型 JSON 不能 转换 Newtonsoft | 更新日期: 2023-09-27 18:01:59
我有一个 c# MVC应用程序,它将数据存储为XML文档中的JSON字符串,也存储在MySQL DB表中。
最近我收到要求在MySQL数据库字段中存储JSON字符串,通过Newtonsoft转换为 c#对象。Json,所以我决定实现一个TypeConverter来将Json字符串转换为自定义c#模型。
不幸的是,当TypeConverter属性被添加到我的c#模型:
时,我不能在我的解决方案中的任何地方使用以下命令来反序列化我的JSON字符串:JsonConvert.DeserializeObject<Foo>(json);
删除属性解决了这个问题,但是这阻止了我将MySQL数据库字段转换为自定义c#对象。
这是我的 c#模型添加了TypeConverter属性:
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<Foo>(s);
return f;
}
return base.ConvertFrom(context, culture, value);
}
}
}
当我将属性添加到Foo类时,我收到以下错误:
无法将当前JSON对象(例如{"name":"value"})反序列化为类型'Models '。
要修复此错误,要么将JSON更改为JSON字符串值,要么更改反序列化类型,使其成为可以从JSON对象反序列化的正常。net类型(例如,不是像整数这样的原始类型,也不是像数组或列表这样的集合类型)。还可以将JsonObjectAttribute添加到类型中,以强制它从JSON对象进行反序列化。
我使用以下字符串(无需添加TypeConverter属性即可完美工作):
"{'"Foo'":{'"a'":true,'"b'":false,'"c'":false}}"
不知道这里发生了什么,有什么想法吗?
多谢! !
<标题> 更新我已经发现,我也有MVC API控制器上的动作问题,接受测试类与Foo作为属性或控制器上接受Foo作为对象当TypeConverter属性添加到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<Foo>(json);
但以下将:
var test = JsonConvert.DeserializeObject<TestClass>(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尝试递归地相互调用。
TypeConverter
。并回落到默认序列化,同时在所有其他情况下保留使用TypeConverter
。这有点棘手,因为没有Json。. NET属性,你可以应用它来禁用类型转换器,相反,你需要一个特殊的契约解析器加上一个特殊的JsonConverter
来使用它:
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);
}
}
并像这样使用:
[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);
}
}
小提琴例子。
最后,您可能还应该在类型转换器中实现ConvertTo
方法,参见如何:实现类型转换器。
避免这种行为的简单方法是从转换检查中删除OR即remove || 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);
}
}
}
如果您有一个结构体而不是类,那么当尝试(反)序列化Nullable<Foo>
时,接受的答案仍然会进入无限递归。
避免对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);
}