How to serialize or deserialize a JSON Object to a certain depth in C#?

后端 未结 4 1068
鱼传尺愫
鱼传尺愫 2020-12-05 14:03

I only want the first depth level of an object (I do not want any children). I am willing to use any library available. Most libraries will merely throw an exception when

4条回答
  •  广开言路
    2020-12-05 14:56

    If you want to use this in a ASP.NET Core project, maybe you cant implement your own JsonTextWriter. But you can custom the DefaultContractResolver and IValueProvider

    using Newtonsoft.Json;
    using Newtonsoft.Json.Serialization;
    using System;
    using System.Collections.Generic;
    using System.Reflection;
    using System.Linq;
    
    namespace customserialization
    {
        /// 
        /// IValueProvider personalizado  para manejar max depth level
        /// 
        public class CustomDynamicValueProvider : DynamicValueProvider, IValueProvider
        {
            MemberInfo _memberInfo;
            MaxDepthHandler _levelHandler;
    
            public CustomDynamicValueProvider(MemberInfo memberInfo, MaxDepthHandler levelHandler) : base(memberInfo)
            {
                _memberInfo = memberInfo;
                _levelHandler = levelHandler;
            }
    
            public new object GetValue(object target)
            {
                //Si el valor a serializar es un objeto se incrementa el nivel de profundidad. En el caso de las listas el nivel se incrementa en el evento OnSerializing
                if (((PropertyInfo)_memberInfo).PropertyType.IsClass) this._levelHandler.IncrementLevel();
    
                var rv = base.GetValue(target);
    
                //Al finalizar la obtención del valor se decrementa. En el caso de las listas el nivel se decrementa en el evento OnSerialized
                if (((PropertyInfo)_memberInfo).PropertyType.IsClass) this._levelHandler.DecrementLevel();
    
                return rv;
            }
        }
    
        /// 
        /// Maneja los niveles de serialización
        /// 
        public class MaxDepthHandler
        {
            int _maxDepth;
            int _currentDepthLevel;
    
            /// 
            /// Nivel actual
            /// 
            public int CurrentDepthLevel { get { return _currentDepthLevel; } }
    
            public MaxDepthHandler(int maxDepth)
            {
                this._currentDepthLevel = 1;
                this._maxDepth = maxDepth;
            }
    
            /// 
            /// Incrementa el nivel actual
            /// 
            public void IncrementLevel()
            {
                this._currentDepthLevel++;
            }
    
            /// 
            /// Decrementa el nivel actual
            /// 
            public void DecrementLevel()
            {
                this._currentDepthLevel--;
            }
    
            /// 
            /// Determina si se alcanzó el nivel actual
            /// 
            /// 
            public bool IsMaxDepthLevel()
            {
                return !(this._currentDepthLevel < this._maxDepth);
            }
        }
    
        public class ShouldSerializeContractResolver : DefaultContractResolver
        {
    
            MaxDepthHandler _levelHandler;
    
            public ShouldSerializeContractResolver(int maxDepth)
            {
                this._levelHandler = new MaxDepthHandler(maxDepth);
            }
    
    
            void OnSerializing(object o, System.Runtime.Serialization.StreamingContext context)
            {
                //Antes de serializar una lista se incrementa el nivel. En el caso de los objetos el nivel se incrementa en el método GetValue del IValueProvider
                if (o.GetType().IsGenericList())
                    _levelHandler.IncrementLevel();
            }
    
            void OnSerialized(object o, System.Runtime.Serialization.StreamingContext context)
            {
                //Despues de serializar una lista se decrementa el nivel. En el caso de los objetos el nivel se decrementa en el método GetValue del IValueProvider
                if (o.GetType().IsGenericList())
                    _levelHandler.DecrementLevel();
            }
    
            protected override JsonContract CreateContract(Type objectType)
            {
                var contract = base.CreateContract(objectType);
                contract.OnSerializingCallbacks.Add(new SerializationCallback(OnSerializing));
                contract.OnSerializedCallbacks.Add(new SerializationCallback(OnSerialized));
    
                return contract;
            }
    
    
            protected override IValueProvider CreateMemberValueProvider(MemberInfo member)
            {
                var rv = base.CreateMemberValueProvider(member);
    
                if (rv is DynamicValueProvider) //DynamicValueProvider es el valueProvider usado en general
                {
                    //Utilizo mi propio ValueProvider, que utilizar el levelHandler
                    rv = new CustomDynamicValueProvider(member, this._levelHandler);
                }
    
                return rv;
            }
    
            protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
            {
                JsonProperty property = base.CreateProperty(member, memberSerialization);
    
                var isObjectOrList = ((PropertyInfo)member).PropertyType.IsGenericList() || ((PropertyInfo)member).PropertyType.IsClass;
    
    
    
                property.ShouldSerialize =
                        instance =>
                        {
                            var shouldSerialize = true;
                            //Si se alcanzo el nivel maximo y la propiedad (member) actual a serializar es un objeto o lista no se serializa (shouldSerialize = false)
                            if (_levelHandler.IsMaxDepthLevel() && isObjectOrList)
                                shouldSerialize = false;                        
    
                            return shouldSerialize;
                        };
    
                return property;
            }
    
    
    
        }
    
        public static class Util
        {
            public static bool IsGenericList(this Type type)
            {
                foreach (Type @interface in type.GetInterfaces())
                {
                    if (@interface.IsGenericType)
                    {
                        if (@interface.GetGenericTypeDefinition() == typeof(ICollection<>))
                        {
                            // if needed, you can also return the type used as generic argument
                            return true;
                        }
                    }
                }
                return false;
            }
        }
    }
    

    and use this in your controller

            [HttpGet]
            public IActionResult TestJSON()
            {
                var obj = new Thing
                {
                    id = 1,
                    reference = new Thing
                    {
                        id = 2,
                        reference = new Thing
                        {
                            id = 3,
                            reference = new Thing
                            {
                                id = 4
                            }
                        }
                    }
                };
                var settings = new JsonSerializerSettings()
                {
                    ContractResolver = new ShouldSerializeContractResolver(2),
                };
    
                return new JsonResult(obj, settings);
    
            }
    

提交回复
热议问题