Generic method of modifying JSON before being returned to client

后端 未结 2 519
渐次进展
渐次进展 2020-12-07 03:54

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.

2条回答
  •  执念已碎
    2020-12-07 04:21

    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.

    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))]
    

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

    Thus:

    public class TestConverter : JsonConverter
    {
        [ThreadStatic]
        static Stack typeStack;
    
        static Stack TypeStack { get { return typeStack = (typeStack ?? new Stack()); } }
    
        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 : IDisposable
        {
            readonly Stack stack;
    
            public PushValue(T value, Stack 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(this Stack stack)
        {
            if (stack == null)
                throw new ArgumentNullException();
            if (stack.Count == 0)
                return default(T);
            return stack.Peek();
        }
    
        public static PushValue PushUsing(this Stack stack, T value)
        {
            if (stack == null)
                throw new ArgumentNullException();
            return new PushValue(value, stack);
        }
    }
    

    In your case TBaseType would be Inua.WebApi.Authentication.IUser.

    Prototype fiddle.

提交回复
热议问题