我有一个 C#MVC 应用程序,该应用程序将数据作为JSON字符串存储在XML文档以及MySQL DB表中。
最近,我收到了 将JSON字符串存储在MySQL数据库字段中的要求 ,可以 通过Newtonsoft.Json 将其转换为 C#对象 ,所以我决定实现TypeConverter来将JSON字符串转换为自定义C#模型。
不幸的是,当 TypeConverter属性添加到我的C#模型中 时,我无法在解决方案中的任何地方使用以下命令来反序列化JSON字符串:
JsonConvert.DeserializeObject<Foo>(json);
删除属性可以解决问题,但是这使我无法将MySQL DB字段转换为自定义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.Foo’,因为该类型需要JSON字符串值才能正确反序列化。
要解决此错误,可以将JSON更改为JSON字符串值,或者更改反序列化的类型,使其成为可以反序列化的普通.NET类型(例如,不是整数的原始类型,而不是数组或列表的集合类型)从JSON对象。 还可以将JsonObjectAttribute添加到类型中,以强制其从JSON对象反序列化。
我正在使用以下字符串(无需添加TypeConverter属性即可完美运行):
"{\"Foo\":{\"a\":true,\"b\":false,\"c\":false}}"
不知道这是怎么回事,有什么想法吗?
非常感谢!!!
我发现,当 TypeConverter属性* 添加到Foo类时,在接受 带有Foo作为属性 的 测试类的 MVC API控制器上 ,或者在接受 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; } }
当上述任一操作通过具有以下结构的AJAX接收JSON时,TypeConverter可能会导致覆盖WebAPI模型绑定的问题并返回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也不对应于您的class Foo,它对应于某些包含Foo属性的容器类,例如:
TypeConverter
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);
我怀疑这只是问题中的一个错误,因此我假设您要反序列化包含property的类Foo。
您看到的主要问题是Json.NET 将尝试使用TypeConverterif(如果存在)将要序列化的类转换为JSON字符串。从文档:
基本类型 .Net :(TypeConverter可转换为String) JSON:String
基本类型
.Net :(TypeConverter可转换为String) JSON:String
但是在您的JSON中,Foo它不是JSON字符串,而是JSON 对象 ,因此一旦应用类型转换器,反序列化将失败。嵌入的字符串如下所示:
{"Foo":"{\"a\":true,\"b\":false,\"c\":false}"}
请注意如何将所有引号转义。并且即使您更改了Foo对象的JSON格式以与此匹配,您的反序列化仍将失败,因为TypeConverterand和Json.NET尝试递归调用彼此。
因此,您需要做的是全局禁用TypeConverterJson.NET的使用,并退回到默认序列化,同时保留TypeConverter在所有其他情况下的使用。这有点棘手,因为没有可用于禁止使用类型转换器的Json.NET属性,而是需要一个特殊的协定解析器以及一个特殊的协定解析器JsonConverter来使用它:
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在类型转换器中实现该方法,请参见如何:实现类型转换器。
ConvertTo