我有一个 C#MVC 应用程序,它将数据作为JSON字符串存储在XML文档中,也存储在MySQL数据库表中。
最近我收到了在MySQL数据库字段中存储JSON字符串的要求,要通过Newtonsoft.Json 转换为 C#对象,所以我决定实现一个TypeConverter将JSON字符串转换为自定义C#模型。
不幸的是,当 TypeConverter属性添加到我的C#模型时,我无法在我的解决方案中的任何地方使用以下命令来反序列化我的JSON字符串:
JsonConvert.DeserializeObject<Foo>(json);
删除属性可以解决问题,但这会阻止我将MySQL数据库字段转换为自定义C#对象。
这是添加了TypeConverter属性的 C#Model :
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对象(例如{&#34; name&#34;:&#34; value&#34;})反序列化为类型&#39; Models.Foo&#39;因为该类型需要JSON字符串值才能正确反序列化。
要修复此错误,请将JSON更改为JSON字符串值或更改反序列化类型,使其成为普通的.NET类型(例如,不是像整数这样的基本类型,而不是类似于数组的集合类型或List)可以从JSON对象反序列化。 JsonObjectAttribute也可以添加到类型中以强制它从JSON对象反序列化。
我正在使用以下字符串(无需添加TypeConverter属性即可正常工作):
"{\"Foo\":{\"a\":true,\"b\":false,\"c\":false}}"
不确定这里发生了什么,有什么想法吗?
非常感谢!!!
我发现我对 MVC API控制器上的操作存在问题,这些操作接受测试类与Foo作为属性或接受的控制器将 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属性的情况下也能正常工作。
任何进一步的帮助非常感谢!!
非常感谢。
答案 0 :(得分:20)
这里有一些事情发生。首先,一个初步问题:即使没有应用TypeConverter
,您的JSON也不对应于您的类Foo
,它对应于包含Foo
属性的某个容器类,例如:< / p>
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字符串。来自docs:
原始类型
.Net:
TypeConverter
(可兑换成字符串)
JSON:String
但是在您的JSON中,Foo
不是JSON字符串,它是JSON 对象,因此一旦应用了类型转换器,反序列化就会失败。嵌入的字符串如下所示:
{"Foo":"{\"a\":true,\"b\":false,\"c\":false}"}
注意所有报价是如何被转义的。即使你更改了Foo
对象的JSON格式以匹配它,你的反序列化仍然会失败,因为TypeConverter
和Json.NET试图以递归方式相互调用。
因此,您需要做的是全局禁用Json.NET对TypeConverter
的使用,并回退到默认序列化,同时在所有其他情况下保留TypeConverter
的使用。这有点棘手,因为没有Json.NET attribute可以申请禁用类型转换器,而是需要一个特殊的合约解析器和一个特殊的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);
}
}
示例fiddle。
最后,您可能还应该在类型转换器中实现ConvertTo
方法,请参阅How to: Implement a Type Converter。
答案 1 :(得分:0)
避免此行为的简单方法是从转换检查中删除OR 即删除|| destinationType == typeof(string)
以下示例..
JsonAnySetter
答案 2 :(得分:0)
如果您的结构不是类,那么在尝试对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);
}