【问题标题】:Deserialize non-writeable array property with Newtonsoft Json使用 Newtonsoft Json 反序列化不可写的数组属性
【发布时间】:2022-11-27 09:28:08
【问题描述】:

我有一个这样的类,我想序列化和反序列化

using Newtonsoft.Json;

TestClass testClass = new TestClass();
testClass.Foo = "Foo";
testClass.Bar[0] = 3;

// to JSON
string json = JsonConvert.SerializeObject(testClass);
// and back
TestClass result = JsonConvert.DeserializeObject<TestClass>(json)!;

/// <summary>
/// External class that I cannot change
/// </summary>
public class TestClass
{
    public string Foo { get; set; }

    public int[] Bar { get; } = new int[3];
}

序列化效果很好,但在反序列化时“Bar”不可写,因此被跳过并具有默认值。 有没有办法告诉 Json.net 逐个元素反序列化(可能是真正的数组?)数组并将其设置为相应数组索引的值?

【问题讨论】:

  • 我想到了两种方法:(1) 在 TestClass 中声明一个构造函数(与 [JsonConstructor] 属性一起使用时可以是私有的),它允许反序列化器将反序列化的数组传递到 TestClass 实例中。然后,您还需要声明一个公共参数构造函数。 (2) 使用 List<int> 而不是 int[]。由您选择一个与项目的整体代码设计更匹配的...
  • 我没有机会改变 TestClass。它是外部库的一部分
  • 好吧。那么我会同意下面 BWA 的回答中给出的建议。 (我很抱歉忽略了之前代码示例中的相应代码注释。)

标签: c# json.net


【解决方案1】:

至少对于这个简单的例子,我有一个解决方案。必须在更复杂的结构上进行测试。

public class TestConverter : JsonConverter
{
    private bool skipOnce = false;

    public override bool CanConvert(Type objectType)
    {
        if (skipOnce)
        {
            skipOnce = false;
            return false;
        }

        return objectType.GetProperties().Any(prop => !prop.CanWrite && prop.PropertyType.IsArray);
    }

    public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
    {
        skipOnce = true; // our manual serializer.Deserialize will cause endless recursion otherwise

        JObject jo = JObject.Load(reader);
        reader = jo.CreateReader(); // reset reader
        var obj = serializer.Deserialize(reader, objectType);

        var readOnlyArrayProperties = objectType.GetProperties().Where(prop => !prop.CanWrite && prop.PropertyType.IsArray);
        foreach (var readOnlyProperty in readOnlyArrayProperties)
        {
            var readOnlyArray = (Array)readOnlyProperty.GetValue(obj)!;
            Type elementType = readOnlyArray.GetType().GetElementType()!;
            var jsonArrayProp = (JArray)jo[readOnlyProperty.Name]!;
            
            for (int i = 0; i < readOnlyArray.Length; i++)
            {
                readOnlyArray.SetValue(jsonArrayProp[i].ToObject(elementType, serializer), i);
            }
        }

        return obj;
    }

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

基本思想:如果我们在当前对象中有只读数组属性,我们手动执行序列化程序。反序列化,另外我们为每个数组索引设置值。

[编辑] 复杂结构测试后:似乎更复杂。有时会设置existingValue。在这些情况下,json.net 似乎忽略了我们返回的对象。填充到existingValue 乍一看似乎有效。

【讨论】:

    【解决方案2】:

    你可以试试这个代码

        var jsonObj=JObject.Parse(json);
        TestClass result=jsonObj.ToObject<TestClass>();
    
        for (var i=0; i <  ((JArray) jsonObj["Bar"]).Count; i++)
            result.Bar[i]= (int) jsonObj["Bar"][i];
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-10-13
      • 2017-10-16
      • 1970-01-01
      相关资源
      最近更新 更多