gpt4 book ai didi

c# - 反序列化 JSON 时如何将 ObjectCreationHandling.Replace 应用于选定的属性?

转载 作者:太空狗 更新时间:2023-10-29 19:59:29 24 4
gpt4 key购买 nike

我有一个包含 List<Tuple<int, int, int>> 的类其默认构造函数分配列表并用一些默认值填充它的属性,例如:

public class Configuration
{
public List<Tuple<int, int, int>> MyThreeTuple { get; set; }

public Configuration()
{
MyThreeTuple = new List<Tuple<int, int, int>>();
MyThreeTuple.Add(new Tuple<int, int, int>(-100, 20, 501));
MyThreeTuple.Add(new Tuple<int, int, int>(100, 20, 864));
MyThreeTuple.Add(new Tuple<int, int, int>(500, 20, 1286));
}
}

当我使用 Json.NET 从 JSON 反序列化此类的实例时,JSON 的值被添加到列表中,而不是替换列表中的项目,导致列表有太多值。 Json.Net calls property getter during deserialization of list, resulting in duplicate items 中给出了此问题的解决方案.

var settings = new JsonSerializerSettings { ObjectCreationHandling = ObjectCreationHandling.Replace };
var config = JsonConvert.DeserializeObject<Configuration>(jsonString, settings);

这会导致 Json.NET 为反序列化的所有内容分配新实例。

然而,这引入了一个额外的问题:我的类存在于一个更大的对象图中,而图中的某些类型没有默认构造函数。它们是由包含类中的构造函数构造的。如果我使用 ObjectCreationHandling = ObjectCreationHandling.Replace , Json.NET 无法尝试构造这些类型的实例,但出现以下异常:

Unable to find a constructor to use for the type MySpecialType. A class 
should either have a default constructor, one constructor with arguments
or a constructor marked with the JsonConstructor attribute.

如何申请ObjectCreationHandling.Replace有选择地针对我的对象图中的某些属性,而不是其他属性?

最佳答案

您有一些替代方法可以强制替换而不是重复使用您的列表:

  1. 您可以添加 attribute到列表属性,表明它应该被替换而不是重用:

    public class Configuration
    {
    [JsonProperty(ObjectCreationHandling = ObjectCreationHandling.Replace)]
    public List<Tuple<int, int, int>> MyThreeTuple { get; set; }
    }
  2. 您可以使用数组代替列表,因为数组总是会被替换。如果您的列表应该始终包含三个项目并且从不调整大小,这可能是有意义的:

    public class Configuration
    {
    public Tuple<int, int, int>[] MyThreeTuple { get; set; }

    public Configuration()
    {
    MyThreeTuple = new[]
    {
    new Tuple<int, int, int>(-100, 20, 501),
    new Tuple<int, int, int>(100, 20, 864),
    new Tuple<int, int, int>(500, 20, 1286),
    };
    }
    }
  3. 如果您不希望您的类定义依赖于 Json.NET,您可以自定义 JsonConverter在反序列化时清除列表:

    public class ConfigurationConverter : JsonConverter
    {
    public override bool CanConvert(Type objectType)
    {
    return typeof(Configuration).IsAssignableFrom(objectType);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
    var config = (existingValue as Configuration ?? (Configuration)serializer.ContractResolver.ResolveContract(objectType).DefaultCreator());
    if (config.MyThreeTuple != null)
    config.MyThreeTuple.Clear();
    serializer.Populate(reader, config);
    return config;
    }

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

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
    throw new NotImplementedException();
    }
    }

    然后将其与以下 JsonSerializerSettings 一起使用:

    var settings = new JsonSerializerSettings { Converters = new JsonConverter[] { new ConfigurationConverter() } };
  4. 如果您希望所有 列表属性被替换而不是重复使用,您可以自定义ContractResolver。这样做:

    public class ListReplacementContractResolver : DefaultContractResolver
    {
    // As of 7.0.1, Json.NET suggests using a static instance for "stateless" contract resolvers, for performance reasons.
    // http://www.newtonsoft.com/json/help/html/ContractResolver.htm
    // http://www.newtonsoft.com/json/help/html/M_Newtonsoft_Json_Serialization_DefaultContractResolver__ctor_1.htm
    // "Use the parameterless constructor and cache instances of the contract resolver within your application for optimal performance."
    static readonly ListReplacementContractResolver instance;

    // Using a static constructor enables fairly lazy initialization. http://csharpindepth.com/Articles/General/Singleton.aspx
    static ListReplacementContractResolver() { instance = new ListReplacementContractResolver(); }

    public static ListReplacementContractResolver Instance { get { return instance; } }

    protected ListReplacementContractResolver() : base() { }

    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
    var jsonProperty = base.CreateProperty(member, memberSerialization);
    if (jsonProperty.ObjectCreationHandling == null && jsonProperty.PropertyType.GetListType() != null)
    jsonProperty.ObjectCreationHandling = ObjectCreationHandling.Replace;
    return jsonProperty;
    }
    }

    public static class TypeExtensions
    {
    public static Type GetListType(this Type type)
    {
    while (type != null)
    {
    if (type.IsGenericType)
    {
    var genType = type.GetGenericTypeDefinition();
    if (genType == typeof(List<>))
    return type.GetGenericArguments()[0];
    }
    type = type.BaseType;
    }
    return null;
    }
    }

    然后通过以下设置使用它:

    var settings = new JsonSerializerSettings { ContractResolver = ListReplacementContractResolver.Instance };
  5. 如果集合是只获取的(在本例中不是),请参阅 Clear collections before adding items when populating existing objects .

关于c# - 反序列化 JSON 时如何将 ObjectCreationHandling.Replace 应用于选定的属性?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33744236/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com