如何序列化或应对json对象的某个深度?

问题描述 投票:0回答:3
我只想要一个物体的第一深度水平(我不想要任何孩子)。 我愿意使用任何可用的库。 当达到递归深度时,大多数库只会引发例外,而不仅仅是忽略。 如果这不可能,是否可以忽略给定某些数据类型的某些成员的序列化?

Edit: 假设我有一个这样的对象:

class MyObject { String name = "Dan"; int age = 88; List<Children> myChildren = ...(lots of children with lots of grandchildren); }

我想删除任何孩子(甚至复杂类型)以返回这样的对象:

class MyObject { String name = "Dan"; int age = 88; List<Children> myChildren = null; }

	
c# .net json serialization
3个回答
37
投票
json.net

中,使用JsonWriter

和序列化器之间的一些协调。

一个自定义ContractResolver当对象启动时会增加计数器,然后在结束时再次将其降低。
JsonWriter
a自定义

public class CustomJsonTextWriter : JsonTextWriter { public CustomJsonTextWriter(TextWriter textWriter) : base(textWriter) {} public int CurrentDepth { get; private set; } public override void WriteStartObject() { CurrentDepth++; base.WriteStartObject(); } public override void WriteEndObject() { CurrentDepth--; base.WriteEndObject(); } }

对所有将用于验证当前深度的属性应用特殊的谓词。

ContractResolver

以下方法显示了这两个自定义类是如何一起工作的。
ShouldSerialize

以下测试代码分别将最大深度限制为1和2级别。

public class CustomContractResolver : DefaultContractResolver
{
    private readonly Func<bool> _includeProperty;

    public CustomContractResolver(Func<bool> includeProperty)
    {
        _includeProperty = includeProperty;
    }

    protected override JsonProperty CreateProperty(
        MemberInfo member, MemberSerialization memberSerialization)
    {
        var property = base.CreateProperty(member, memberSerialization);
        var shouldSerialize = property.ShouldSerialize;
        property.ShouldSerialize = obj => _includeProperty() &&
                                          (shouldSerialize == null ||
                                           shouldSerialize(obj));
        return property;
    }
}
首先,我想说所有的荣誉都应该去内森·鲍尔奇(Nathan Baulch)。  这是他的答案的改编,并在设置中使用MaxDepth。  感谢您的帮助Nathan!
public static string SerializeObject(object obj, int maxDepth) { using (var strWriter = new StringWriter()) { using (var jsonWriter = new CustomJsonTextWriter(strWriter)) { Func<bool> include = () => jsonWriter.CurrentDepth <= maxDepth; var resolver = new CustomContractResolver(include); var serializer = new JsonSerializer {ContractResolver = resolver}; serializer.Serialize(jsonWriter, obj); } return strWriter.ToString(); } }

使用:

var obj = new Node { Name = "one", Child = new Node { Name = "two", Child = new Node { Name = "three" } } }; var txt1 = SerializeObject(obj, 1); var txt2 = SerializeObject(obj, 2); public class Node { public string Name { get; set; } public Node Child { get; set; } }


您可以使用反射检查对象并制作根据需要更改每个属性值的副本。巧合的是,我刚刚公开了一个新图书馆,这使得这种事情变得非常容易。您可以在这里获取:

Https://github.com/jamietre/iqobjectmapper


there是您要使用的代码的一个示例


2
投票

“映射”方法通过对象的每个属性迭代,并为每个属性调用

// instantiating JsonNetResult to handle circular reference issue. var result = new JsonNetResult { Data = <<The results to be returned>>, JsonRequestBehavior = JsonRequestBehavior.AllowGet, Settings = { ReferenceLoopHandling = ReferenceLoopHandling.Ignore, MaxDepth = 1 } }; return result;
(具有反射信息,例如属性名称,类型等)。该功能返回每个属性的新值。因此,在此示例中,我只是测试每个属性的值,以查看是否是类,如果是的,则返回null;如果没有,请返回原始值。

其他更能表达的方法:

var newInstance = ObjectMapper.Map(obj,(value,del) => {
    return value !=null && value.GetType().IsClass ?
        null :
        value;
    });
在任何一种情况下,

1
投票
的值(以及任何其他非价值类型的属性)都是无效的。您可以轻松地更改此映射中发生的情况的规则。

希望这有帮助。顺便说一句 - 从您的评论中,听起来JSON并不是您的目标,而是您认为可以帮助您实现目标的事情。如果您想结束JSON,只需序列化此输出,例如 var obj = new MyObject(); // map the object to a new dictionary var dict = ObjectMapper.ToDictionary(obj); // iterate through each item in the dictionary, a key/value pair // representing each property foreach (KeyValuePair<string,object> kvp in dict) { if (kvp.Value!=null && kvp.Value.GetType().IsClass) { dict[kvp.Key]=null; } } // map back to an instance var newObject = ObjectMapper.ToNew<MyObject>(dict);

但是,如果这只是达到目的的一种手段,我不会涉及JSON;如果您想留在CLR对象中,则无需将JSON用作中介。

如果您想在ASP.NET核心项目中使用此此此事,也许您无法实现自己的JSontextWriter。但是您可以自定义DefaultContractresolver和Ivalueprovider

newInstance.myChildren

在您的控制器中使用它

string json = JavaScriptSerializer.Serialize(newObject);

也许是System.Text.json的解决方案对某人很有用

using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Linq;

namespace customserialization
{
    /// <summary>
    /// IValueProvider personalizado  para manejar max depth level
    /// </summary>
    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;
        }
    }

    /// <summary>
    /// Maneja los niveles de serialización
    /// </summary>
    public class MaxDepthHandler
    {
        int _maxDepth;
        int _currentDepthLevel;

        /// <summary>
        /// Nivel actual
        /// </summary>
        public int CurrentDepthLevel { get { return _currentDepthLevel; } }

        public MaxDepthHandler(int maxDepth)
        {
            this._currentDepthLevel = 1;
            this._maxDepth = maxDepth;
        }

        /// <summary>
        /// Incrementa el nivel actual
        /// </summary>
        public void IncrementLevel()
        {
            this._currentDepthLevel++;
        }

        /// <summary>
        /// Decrementa el nivel actual
        /// </summary>
        public void DecrementLevel()
        {
            this._currentDepthLevel--;
        }

        /// <summary>
        /// Determina si se alcanzó el nivel actual
        /// </summary>
        /// <returns></returns>
        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;
        }
    }
}

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.