在重新使用对象并替换数组的地方填充对象? [英] Populate object where objects are reused and arrays are replaced?

查看:96
本文介绍了在重新使用对象并替换数组的地方填充对象?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用Json.NET,我想将JObject映射到具有以下行为的.NET对象:

Using Json.NET, I'd like to map a JObject onto a .NET object with the following behavior:

  1. 对于JObject的(非空)数组属性,替换目标对象上的 entire 集合.

  1. For (non-null) array properties of the JObject, replace the entire collection on the target object.

对于JObject的(非空)对象属性,请重用目标对象的属性(如果它不为null),然后仅将提供的属性映射到其上.

For (non-null) object properties of the JObject, reuse the target object's property if it's not null, and map just the provided properties onto it.

JsonSerializer.Populate 似乎是我想要的,因为在此答案中进行了介绍.至于我要寻找的行为,似乎可以通过 JsonSerializerSettings.ObjectCreationHandling . ObjectCreationHandling.Replace针对需求1执行我想要的,而ObjectCreationHandling.Auto针对需求2执行我想要的,但是它将数组项附加到现有集合上.

JsonSerializer.Populate seems to be what I want, as described in this answer. As for the behaviors I'm looking for, it seems I can achieve one or the other, but not both, via JsonSerializerSettings.ObjectCreationHandling. ObjectCreationHandling.Replace does what I want with respect to requirement #1, while ObjectCreationHandling.Auto does what I want with respect to requirement #2, but it appends array items onto the existing collection.

在这里达到这两个要求的推荐方法是什么?

What is the recommended way to achieve both requirements here?

推荐答案

Json.NET将自动替换任何数组或只读集合.要在反序列化时清除读写集合,可以创建一个自定义合同解析器,向每个可修改的集合添加 OnDeserializingCallback 反序列化开始时清除集合.清除集合而不是完全替换它可以处理仅获取集合的情况,例如:

Json.NET will automatically replace any arrays or read-only collections. To clear out read-write collections when deserializing, you could create a custom contract resolver that adds an OnDeserializingCallback to every modifiable collection that clears the collection when deserialization begins. Clearing the collection rather that replacing it outright handles cases where the collection is get-only, for instance:

public class RootObject
{
    readonly HashSet<int> hashSet = new HashSet<int>();
    public HashSet<int> HashSetValues { get { return this.hashSet; } }
}

合同解析器如下:

public class CollectionClearingContractResolver : DefaultContractResolver
{
    static void ClearGenericCollectionCallback<T>(object o, StreamingContext c)
    {
        var collection = o as ICollection<T>;
        if (collection == null || collection is Array || collection.IsReadOnly)
            return;
        collection.Clear();
    }

    static SerializationCallback ClearListCallback = (o, c) =>
        {
            var collection = o as IList;
            if (collection == null || collection is Array || collection.IsReadOnly)
                return;
            collection.Clear();
        };

    protected override JsonArrayContract CreateArrayContract(Type objectType)
    {
        var contract = base.CreateArrayContract(objectType);
        if (!objectType.IsArray)
        {
            if (typeof(IList).IsAssignableFrom(objectType))
            {
                contract.OnDeserializingCallbacks.Add(ClearListCallback);
            }
            else if (objectType.GetCollectItemTypes().Count() == 1)
            {
                MethodInfo method = typeof(CollectionClearingContractResolver).GetMethod("ClearGenericCollectionCallback", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
                MethodInfo generic = method.MakeGenericMethod(contract.CollectionItemType);
                contract.OnDeserializingCallbacks.Add((SerializationCallback)Delegate.CreateDelegate(typeof(SerializationCallback), generic));
            }
        }

        return contract;
    }
}

public static class TypeExtensions
{
    public static IEnumerable<Type> GetInterfacesAndSelf(this Type type)
    {
        if (type == null)
            throw new ArgumentNullException();
        if (type.IsInterface)
            return new[] { type }.Concat(type.GetInterfaces());
        else
            return type.GetInterfaces();
    }

    public static IEnumerable<Type> GetCollectItemTypes(this Type type)
    {
        foreach (Type intType in type.GetInterfacesAndSelf())
        {
            if (intType.IsGenericType
                && intType.GetGenericTypeDefinition() == typeof(ICollection<>))
            {
                yield return intType.GetGenericArguments()[0];
            }
        }
    }
}

public static class JsonExtensions
{
    public static void Populate<T>(this JToken value, T target) where T : class
    {
        value.Populate(target, null);
    }

    public static void Populate<T>(this JToken value, T target, JsonSerializerSettings settings) where T : class
    {
        using (var sr = value.CreateReader())
        {
            JsonSerializer.CreateDefault(settings).Populate(sr, target);
        }
    }
}

然后使用它,请执行以下操作:

Then to use it, do:

var settings = new JsonSerializerSettings
{
    ContractResolver = new CollectionClearingContractResolver(),
};
jObject.Populate(rootObject, settings);

示例小提琴.

当反序列化在其默认构造函数中填充集合的对象时,这种合同解析器也很有用,例如反序列化会导致List-Entries的副本.

Such a contract resolver would also be useful when deserializing objects that populate collections in their default constructor, as in Deserialization causes copies of List-Entries.

这篇关于在重新使用对象并替换数组的地方填充对象?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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