json.net limit maxdepth when serializing

混江龙づ霸主 提交于 2020-05-09 02:09:07

问题


We're using Asp.Net WebAPI with Entity Framework (with lazy loading) and using Json.net for serializing the data to json before returning the data to the client.

We are experiencing intermittent sudden spikes in memory usage which we suspect might originate with json.net not recognizing reference loops when serializing data (since Entity Framework might be doing some lazy loading voodoo with proxy classes which goes under the radar of json.net)

I thought I'd limit how deep Json.net was allowed to go to serialize data (at least then we'd get a sensible exception when this happens so we could fix it in the data model), but I soon discovered that the MaxDepth property of JsonSerializerSettings only kicks in when DEserializing objects.

Is there any known way of imposing a limit on json.net when serializing?


回答1:


I can't think of a way to do this out-of-the-box with Json.NET, since (as you correctly observe) MaxDepth is ignored when serializing. What you could do is to subclass JsonTextWriter and do the checks yourself:

public class MaxDepthJsonTextWriter : JsonTextWriter
{
    public int? MaxDepth { get; set; }
    public int MaxObservedDepth { get; private set; }

    public MaxDepthJsonTextWriter(TextWriter writer, JsonSerializerSettings settings)
        : base(writer)
    {
        this.MaxDepth = (settings == null ? null : settings.MaxDepth);
        this.MaxObservedDepth = 0;
    }

    public MaxDepthJsonTextWriter(TextWriter writer, int? maxDepth)
        : base(writer)
    {
        this.MaxDepth = maxDepth;
    }

    public override void WriteStartArray()
    {
        base.WriteStartArray();
        CheckDepth();
    }

    public override void WriteStartConstructor(string name)
    {
        base.WriteStartConstructor(name);
        CheckDepth();
    }

    public override void WriteStartObject()
    {
        base.WriteStartObject();
        CheckDepth();
    }

    private void CheckDepth()
    {
        MaxObservedDepth = Math.Max(MaxObservedDepth, Top);
        if (Top > MaxDepth)
            throw new JsonSerializationException(string.Format("Depth {0} Exceeds MaxDepth {1} at path \"{2}\"", Top, MaxDepth, Path));
    }
}

Then, to manually generate a JSON string, you would use it like this:

var settings = new JsonSerializerSettings { MaxDepth = 10 };
string json;
try
{
    using (var writer = new StringWriter())
    {
        using (var jsonWriter = new MaxDepthJsonTextWriter(writer, settings))
        {
            JsonSerializer.Create(settings).Serialize(jsonWriter, myClass);
            // Log the MaxObservedDepth here, if you want to.
        }
        json = writer.ToString();
    }
    Debug.WriteLine(json);
}
catch (Exception ex)
{
    Debug.WriteLine(ex);
    throw;
}

Demo fiddle here.

Since your tags include web-api, if you want to do this check inside web API calls, you could follow Rick Strahl's instructions to create a custom MediaTypeFormatter for JSON: Using an alternate JSON Serializer in ASP.NET Web API; then use the code above in the OnWriteToStreamAsync method when generating the json string.



来源:https://stackoverflow.com/questions/29651433/json-net-limit-maxdepth-when-serializing

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!