JSON.Net在使用[JsonConvert()]时抛出StackOverflowException
我写了这个简单的代码来将类序列化为扁平化,但是当我使用[JsonConverter(typeof(FJson))]
注释时,它会引发StackOverflowException。 如果我手动调用SerializeObject
,它工作正常。
我如何在Annotation模式下使用JsonConvert:
class Program
{
static void Main(string[] args)
{
A a = new A();
a.id = 1;
a.b.name = "value";
string json = null;
// json = JsonConvert.SerializeObject(a, new FJson()); without [JsonConverter(typeof(FJson))] annotation workd fine
// json = JsonConvert.SerializeObject(a); StackOverflowException
Console.WriteLine(json);
Console.ReadLine();
}
}
//[JsonConverter(typeof(FJson))] StackOverflowException
public class A
{
public A()
{
this.b = new B();
}
public int id { get; set; }
public string name { get; set; }
public B b { get; set; }
}
public class B
{
public string name { get; set; }
}
public class FJson : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
JToken t = JToken.FromObject(value);
if (t.Type != JTokenType.Object)
{
t.WriteTo(writer);
return;
}
JObject o = (JObject)t;
writer.WriteStartObject();
WriteJson(writer, o);
writer.WriteEndObject();
}
private void WriteJson(JsonWriter writer, JObject value)
{
foreach (var p in value.Properties())
{
if (p.Value is JObject)
WriteJson(writer, (JObject)p.Value);
else
p.WriteTo(writer);
}
}
public override object ReadJson(JsonReader reader, Type objectType,
object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override bool CanConvert(Type objectType)
{
return true; // works for any type
}
}
对于调用JToken.FromObject
生成“默认”序列化,然后修改生成的JToken
进行输出的转换器,Json.NET没有方便的支持 - 这正是因为您观察到的StackOverflowException
将会发生。
一种解决方法是使用线程静态布尔值临时禁用递归调用中的转换器。 使用线程静态是因为在某些情况下,包括asp.net-web-api,JSON转换器的实例将在线程之间共享。 在这种情况下,通过实例属性禁用转换器将不是线程安全的。
public class FJson : JsonConverter
{
[ThreadStatic]
static bool disabled;
// Disables the converter in a thread-safe manner.
bool Disabled { get { return disabled; } set { disabled = value; } }
public override bool CanWrite { get { return !Disabled; } }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
JToken t;
using (new PushValue<bool>(true, () => Disabled, (canWrite) => Disabled = canWrite))
{
t = JToken.FromObject(value, serializer);
}
if (t.Type != JTokenType.Object)
{
t.WriteTo(writer);
return;
}
JObject o = (JObject)t;
writer.WriteStartObject();
WriteJson(writer, o);
writer.WriteEndObject();
}
private void WriteJson(JsonWriter writer, JObject value)
{
foreach (var p in value.Properties())
{
if (p.Value is JObject)
WriteJson(writer, (JObject)p.Value);
else
p.WriteTo(writer);
}
}
public override object ReadJson(JsonReader reader, Type objectType,
object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override bool CanConvert(Type objectType)
{
return true; // works for any type
}
}
public struct PushValue<T> : IDisposable
{
Func<T> getValue;
Action<T> setValue;
T oldValue;
public PushValue(T value, Func<T> getValue, Action<T> setValue)
{
if (getValue == null || setValue == null)
throw new ArgumentNullException();
this.getValue = getValue;
this.setValue = setValue;
this.oldValue = getValue();
setValue(value);
}
#region IDisposable Members
// By using a disposable struct we avoid the overhead of allocating and freeing an instance of a finalizable class.
public void Dispose()
{
if (setValue != null)
setValue(oldValue);
}
#endregion
}
注意这个转换器只能写入; 阅读没有实施。
顺便说一句,你写的转换器会创建带有重复名称的JSON:
{
"id": 1,
"name": null,
"name": "value"
}
这虽然不严格违法,但通常被认为是不好的做法
如果你确定你的转换器不会被跨线程共享,你可以使用成员变量:
public class FJson : JsonConverter
{
bool CannotWrite { get; set; }
public override bool CanWrite { get { return !CannotWrite; } }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
JToken t;
using (new PushValue<bool>(true, () => CannotWrite, (canWrite) => CannotWrite = canWrite))
{
t = JToken.FromObject(value, serializer);
}
if (t.Type != JTokenType.Object)
{
t.WriteTo(writer);
return;
}
JObject o = (JObject)t;
writer.WriteStartObject();
WriteJson(writer, o);
writer.WriteEndObject();
}
private void WriteJson(JsonWriter writer, JObject value)
{
foreach (var p in value.Properties())
{
if (p.Value is JObject)
WriteJson(writer, (JObject)p.Value);
else
p.WriteTo(writer);
}
}
public override object ReadJson(JsonReader reader, Type objectType,
object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override bool CanConvert(Type objectType)
{
return true; // works for any type
}
}
在这个方案中,有必要调用JToken.FromObject(Object, JsonSerializer)
并传入串行器,以便使用转换器FJson
的相同实例。 完成此操作后,可以将[JsonConverter(typeof(FJson))]
恢复到您的类A
:
[JsonConverter(typeof(FJson))]
public class A
{
}
我不喜欢上面发布的解决方案,因此我计算了序列化程序如何实际序列化对象并试图将其最小化:
public override void WriteJson( JsonWriter writer, object value, JsonSerializer serializer )
{
JsonObjectContract contract = (JsonObjectContract)serializer.ContractResolver.ResolveContract( value.GetType() );
writer.WriteStartObject();
foreach ( var property in contract.Properties )
{
writer.WritePropertyName( property.PropertyName );
writer.WriteValue( property.ValueProvider.GetValue(value));
}
writer.WriteEndObject();
}
没有堆栈溢出问题,也不需要递归禁用标志。
在阅读(并测试)Paul Kiar&p.kaneman解决方案后,我认为实现WriteJson
似乎是一项具有挑战性的任务。 尽管它适用于大多数情况 - 还有一些尚未覆盖的边缘案例。 例子:
public bool ShouldSerialize*()
方法 null
值 struct
) 这是(只是)另一个尝试:
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
if (ReferenceEquals(value, null)) {
writer.WriteNull();
return;
}
var contract = (JsonObjectContract)serializer
.ContractResolver
.ResolveContract(value.GetType());
writer.WriteStartObject();
foreach (var property in contract.Properties) {
if (property.Ignored) continue;
if (!ShouldSerialize(property, value)) continue;
var property_name = property.PropertyName;
var property_value = property.ValueProvider.GetValue(value);
writer.WritePropertyName(property_name);
if (property.Converter != null && property.Converter.CanWrite) {
property.Converter.WriteJson(writer, property_value, serializer);
} else {
serializer.Serialize(writer, property_value);
}
}
writer.WriteEndObject();
}
private static bool ShouldSerialize(JsonProperty property, object instance) {
return property.ShouldSerialize == null
|| property.ShouldSerialize(instance);
}
链接地址: http://www.djcxy.com/p/37831.html
上一篇: JSON.Net throws StackOverflowException when using [JsonConvert()]