使用[JsonConvert()]时,JSON.Net引发StackOverflowException

使用[JsonConvert()]时,JSON.Net引发StackOverflowException,第1张

使用[JsonConvert()]时,JSON.Net引发StackOverflowException

Json.NET对调用

JToken.FromObject
生成“默认”序列化然后修改结果
JToken
的输出的转换器不提供方便的支持-
正是因为您所观察
StackOverflowException
到的递归调用
JsonConverter.WriteJson()
会发生。

一种解决方法 是使用线程静态布尔值在递归调用中临时禁用转换器。使用线程静态是因为在某些情况下,包括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{    Action<T> setValue;    T oldValue;    public PushValue(T value, Func<T> getValue, Action<T> setValue)    {        if (getValue == null || setValue == null) throw new ArgumentNullException();        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}

完成此 *** 作后,您可以将还原

[JsonConverter(typeof(FJson))]
到您的班级
A

[JsonConverter(typeof(FJson))]public class A{}

演示小提琴#1 在这里。

第二个更简单的变通办法 是为应用了的类型生成默认序列化,

JsonConverter
它利用了一个事实,即应用于 成员的 转换器会取代应用于
类型
或设置中的转换器。从文档:

使用JsonConverter的优先级是由成员上的属性定义的JsonConverter,然后是由类中的属性定义的JsonConverter,最后是传递给JsonSerializer的所有转换器。

因此,可以通过将单个类型的成员嵌套在DTO内来为您的类型生成默认序列化,该成员的值是您的类型的实例,并且应用了虚拟转换器,除了转换为读取和读取的默认序列化之外,它什么都没做写作。

以下扩展方法和转换器可以完成此工作:

public static partial class JsonExtensions{    public static JToken DefaultFromObject(this JsonSerializer serializer, object value)    {        if (value == null) return JValue.CreateNull();        var dto = Activator.CreateInstance(typeof(DefaultSerializationDTO<>).MakeGenericType(value.GetType()), value);        var root = JObject.FromObject(dto, serializer);        return root["Value"].RemoveFromLowestPossibleParent() ?? JValue.CreateNull();    }    public static object DefaultToObject(this JToken token, Type type, JsonSerializer serializer = null)    {        var oldParent = token.Parent;        var dtoToken = new JObject(new JProperty("Value", token));        var dtoType = typeof(DefaultSerializationDTO<>).MakeGenericType(type);        var dto = (IHasValue)(serializer ?? JsonSerializer.CreateDefault()).Deserialize(dtoToken.CreateReader(), dtoType);        if (oldParent == null) token.RemoveFromLowestPossibleParent();        return dto == null ? null : dto.GetValue();    }    public static JToken RemoveFromLowestPossibleParent(this JToken node)    {        if (node == null) return null;        // If the parent is a JProperty, remove that instead of the token itself.        var contained = node.Parent is JProperty ? node.Parent : node;        contained.Remove();        // Also detach the node from its immediate containing property -- Remove() does not do this even though it seems like it should        if (contained is JProperty) ((JProperty)node.Parent).Value = null;        return node;    }    interface IHasValue    {        object GetValue();    }    [JsonObject(NamingStrategyType = typeof(DefaultNamingStrategy), IsReference = false)]    class DefaultSerializationDTO<T> : IHasValue    {        public DefaultSerializationDTO(T value) { this.Value = value; }        public DefaultSerializationDTO() { }        [JsonConverter(typeof(NoConverter)), JsonProperty(ReferenceLoopHandling = ReferenceLoopHandling.Serialize)]        public T Value { get; set; }        object IHasValue.GetValue() { return Value; }    }}public class NoConverter : JsonConverter{    // NoConverter taken from this answer https://stackoverflow.com/a/39739105/3744182    // To https://stackoverflow.com/questions/39738714/selectively-use-default-json-converter    // By https://stackoverflow.com/users/3744182/dbc    public override bool CanConvert(Type objectType)  { throw new NotImplementedException();  }    public override bool CanRead { get { return false; } }    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { throw new NotImplementedException(); }    public override bool CanWrite { get { return false; } }    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { throw new NotImplementedException(); }}

然后

FJson.WriteJson()
按以下方式使用它:

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer){    JToken t = serializer.DefaultFromObject(value);    // Remainder as before    if (t.Type != JTokenType.Object)    {        t.WriteTo(writer);        return;    }    JObject o = (JObject)t;    writer.WriteStartObject();    WriteJson(writer, o);    writer.WriteEndObject();}

这种方法的优点是:

  1. 它不依赖于递归禁用转换器,因此可以与递归数据模型一起正常工作。

  2. 它不需要重新实现从对象属性序列化对象的整个逻辑。

演示小提琴#2 在这里。

笔记

  • 两种转换器版本都只能处理写入。阅读未实现。

要在解决这个问题相当于 系列化,例如见 Json.NET与JsonConverter自定义序列化-
如何让“默认”的行为

  • 您编写的转换器会创建具有重复名称的JSON:
{  "id": 1,  "name": null,  "name": "value"}

尽管这不是严格非法的,但通常被认为是不良做法,因此应避免使用。



欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5441053.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-11
下一篇 2022-12-11

发表评论

登录后才能评论

评论列表(0条)

保存