之前修改JSON的通用方法的返回给客户端 [英] Generic method of modifying JSON before being returned to client

查看:147
本文介绍了之前修改JSON的通用方法的返回给客户端的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我后被返回给客户端,可以让我修改对象的JSON泛型方法,尤其是去除返回的对象的某些属性。类似于建议这里

I'm after a generic method that allows me to modify the JSON of an object being returned to the client, specifically the removal of certain properties in returned objects. Similar to what is suggested here.

的修改是非确定性在于它们每个请求的决定的基础上,与用户相关联的规则。因此,这不适合于被缓存的方法。

The modifications are non-deterministic in that they are determined per-request, based on rules associated with the user. So this not suited to a method that is cached.

我查看了几种方法。最明显的选择将是一个JsonConverter,但有这个问题,因为上市这里,<一个href=\"http://stackoverflow.com/questions/26129448/json-net-how-to-customize-serialization-to-insert-a-json-property\">here和<一个href=\"http://stackoverflow.com/questions/12314438/self-referencing-loop-in-json-net-jsonserializer-from-custom-jsonconverter-web\">here.

I've reviewed several methods. The most obvious choice would be a JsonConverter, however there are problems with this, as listed here, here and here.

这种方法的主要问题是调用 JToken.FromObject WriteJson 来获得JSON的具体值,递归地调用同一JsonConverter,产生一个循环

The main problem with this approach is that calling JToken.FromObject in WriteJson to get the JSON for the specific value, recursively calls the same JsonConverter, resulting in a loop.

我试过这里解决上市的变体,它提供了暂时禁用 CanWrite 来prevent的循环问题。然而,它似乎并不为多个并发请求工作。在JsonConverter的单个实例被认为正在发生变化,阅读在不同的时间CanWrite属性的状态,造成的结果不一致多个线程之间共享。

I've tried a variant of the solution listed here which provides a method of temporarily disabling CanWrite to prevent the looping issue. However it doesn't seem to work for more than one concurrent request. A single instance of the JsonConverter is being shared between multiple threads that are changing and reading the state of the CanWrite property at different times, causing inconsistent results.

我也在 WriteJson 使用不同的串行尝试(即除提供给该方法的另外一个),但是,这并不支持递归(因为没有按序列化器'T用我的JsonConverter),所以任何嵌套的项目不我JsonConverter处理。从默认的序列化的转换器收藏中移除我JsonConverter有同样的问题。

I've also tried using a different serializer in WriteJson (i.e. other than the one supplied to the method) however this doesn't support recursion (because that serializer doesn't use my JsonConverter) so any nested items aren't processed by my JsonConverter. Removing my JsonConverter from the default serializer's converters collection has the same problem.

基本上,如果我希望能够处理递归我的模型对象,我会得到自我引用循环的问题。

Basically, if I want to be able to recursively process my model object, I'm going to get the self referencing loop issue.

在理想情况下, JToken.FromObject 将有不 - 选择性呼叫JsonConverter对象本身上,但仍然系列化期间,它适用于所有子对象的某种方式。我半路上通过修改 CanConvert 设置 CanWrite 为true,仅当对象传递给<$修复这个C $ C> CanConvert 是一个不同类型>传递给 WriteJson

Ideally, JToken.FromObject would have some way of selectivly NOT calling the JsonConverter on the object itself, but still applying it to any child objects during serialization. I got half way to fixing this by modifying CanConvert to set CanWrite to true, only if the object passed to CanConvert was a different type to the last object passed to WriteJson.

不过这个工作,我需要每个请求的范围JsonConverter(以上相同的线程的原因),但我看不出如何得到这一点。

However for this to work I would need a per-request scoped JsonConverter (for the same threading reasons above), but I can't see how to get that.

下面是什么,我有一个示例: -

Here is a sample of what I have:-

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Test
{
    public class TestConverter : JsonConverter
    {
        bool CannotWrite { get; set; }

        public override bool CanWrite { get { return !CannotWrite; } }

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            JToken token;

            //----------------------------------------

            // this works; but because it's (i think) creating a new
            // serializer inside the FromObject method
            // which means any nested objects won't get processed

            //token = JToken.FromObject(value);

            //----------------------------------------

            // this creates loop because calling FromObject will cause this
            // same JsonConverter to get called on the same object again

            //token = JToken.FromObject(value, serializer);

            //----------------------------------------

            // this gets around the loop issue, but the JsonConverter will
            // not apply to any nested objects

            //serializer.Converters.Remove(this);
            //token = JToken.FromObject(value, serializer);

            //----------------------------------------

            // see http://stackoverflow.com/a/29720068/1196867
            //
            // this works as it allows us to use the same serializer, but
            // temporarily sets CanWrite to false so the invocation of
            // FromObject doesn't cause a loop
            //
            // this also means we can't process nested objects, however
            // see below in CanConvert for a potential workaround.

            using (new PushValue<bool>(true, () => CannotWrite, (cantWrite) => CannotWrite = cantWrite))
            {
                token = JToken.FromObject(value, serializer);
            }

            // store the type of this value so we can check it in CanConvert when called for any nested objects
            this.currentType = value.GetType();

            //----------------------------------------

            // in practice this would be obtained dynamically
            string[] omit = new string[] { "Name" };

            JObject jObject = token as JObject;

            foreach (JProperty property in jObject.Properties().Where(p => omit.Contains(p.Name, StringComparer.OrdinalIgnoreCase)).ToList())
            {
                property.Remove();
            }

            token.WriteTo(writer);
        }

        private Type currentType;

        public override bool CanConvert(Type objectType)
        {
            if (typeof(Inua.WebApi.Authentication.IUser).IsAssignableFrom(objectType))
            {
                // if objectType is different to the type which is currently being processed,
                // then set CanWrite to true, so this JsonConverter will apply to any nested
                // objects that we want to process
                if (this.currentType != null && this.currentType != objectType)
                {
                    this.CannotWrite = false;
                }

                return true;
            }

            return false;
        }

        public override bool CanRead { get { return false; } }

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            throw new NotImplementedException();
        }
    }
}

选项,我认为: -

Options I've considered:-


  1. 使用自定义JsonConverter,但构建JSON手动,而不是
    利用JToken.FromObject的(增加了很多复杂的)

  2. 使用一个ActionFilterAttribute并除去从属性
    之前序列化模型的(我需要使用反射,每
    要求修改模型对象)

  3. 使用 ShouldSerialzeX()在我的模型执行查找的方法(不容易维护)

  4. 使用自定义ContractResolver的(这从同一个缓存遭受
    问题,即使我用了现已过时的构造函数
    DefaultContractResolver,设置shareCache为false)

  1. Use a custom JsonConverter, but build the JSON manually instead of leveraging JToken.FromObject (adds a lot of complexity)
  2. Using an ActionFilterAttribute and removing properties from the model prior to serialization (I'd need to use reflection for every request to modify the model object)
  3. Using ShouldSerialzeX() methods in my models that perform lookups (not easily maintainable)
  4. Using a custom ContractResolver (this suffers from the same caching issue, even if I use the now obsolete constructor in DefaultContractResolver that sets "shareCache" to false)

任何人都可以提出: -

Can anyone suggest:-


  • 系统的方式,使JsonConverters每个请求

  • 假设它不能被每个请求的提出,一种用JsonConverter来解决线程问题

  • 要JsonConverter的替代,让我在全球范围内检查和修改JSON对象,他们将返回到不依靠大量反射的开销
  • 客户端之前,
  • 别的东西吗?

  • A way to make JsonConverters per-request
  • Assuming it can't be made per-request, a way to fix the threading issue with JsonConverter
  • An alternative to JsonConverter that allows me to globally inspect and modify JSON objects before they are returned to the client that doesn't rely on a lot of reflection overhead
  • Something else?

提前感谢您抽出时间阅读本。

Thanks in advance for taking the time to read this.

推荐答案

一种可能性修复 TestConverter 为多线程,多类型的方案是创建一个类型[ThreadStatic] 堆栈正在连载。然后,在 CanConvert ,返回如果候选类型是相同类型的堆栈顶部的类型。

One possibility to fix the TestConverter for multi-threaded, multi-type scenarios would be to create a [ThreadStatic] stack of types being serialized. Then, in CanConvert, return false if the candidate type is of the same type as the type on top of the stack.

请注意这个的只有当转换器包含在<α的工作原理href=\"http://www.newtonsoft.com/json/help/html/P_Newtonsoft_Json_JsonSerializerSettings_Converters.htm\"相对=nofollow> JsonSerializerSettings.Converters 。如果转换器被直接加到一个类或属性与,比如说,

Note this only works when the converter is included in JsonSerializerSettings.Converters. If the converter is applied directly to a class or property with, say,

    [JsonConverter(typeof(TestConverter<Inua.WebApi.Authentication.IUser>))]

那么仍然会发生无限递归,因为 CanConvert 不要求直接应用转换器。

Then infinite recursion will still occur since CanConvert is not called for directly applied converters.

因此​​:

public class TestConverter<TBaseType> : JsonConverter
{
    [ThreadStatic]
    static Stack<Type> typeStack;

    static Stack<Type> TypeStack { get { return typeStack = (typeStack ?? new Stack<Type>()); } }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        JToken token;

        using (TypeStack.PushUsing(value.GetType()))
        {
            token = JToken.FromObject(value, serializer);
        }

        // in practice this would be obtained dynamically
        string[] omit = new string[] { "Name" };

        JObject jObject = token as JObject;

        foreach (JProperty property in jObject.Properties().Where(p => omit.Contains(p.Name, StringComparer.OrdinalIgnoreCase)).ToList())
        {
            property.Remove();
        }

        token.WriteTo(writer);
    }

    public override bool CanConvert(Type objectType)
    {
        if (typeof(TBaseType).IsAssignableFrom(objectType))
        {
            return TypeStack.PeekOrDefault() != objectType;
        }

        return false;
    }

    public override bool CanRead { get { return false; } }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

public static class StackExtensions
{
    public struct PushValue<T> : IDisposable
    {
        readonly Stack<T> stack;

        public PushValue(T value, Stack<T> stack)
        {
            this.stack = stack;
            stack.Push(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 (stack != null)
                stack.Pop();
        }

        #endregion
    }

    public static T PeekOrDefault<T>(this Stack<T> stack)
    {
        if (stack == null)
            throw new ArgumentNullException();
        if (stack.Count == 0)
            return default(T);
        return stack.Peek();
    }

    public static PushValue<T> PushUsing<T>(this Stack<T> stack, T value)
    {
        if (stack == null)
            throw new ArgumentNullException();
        return new PushValue<T>(value, stack);
    }
}

在你的情况 TBaseType Inua.WebApi.Authentication.IUser

小提琴。

这篇关于之前修改JSON的通用方法的返回给客户端的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆