【问题标题】:Writing to and reading from JSON an array of different types with Newtonsoft使用 Newtonsoft 向 JSON 写入和读取不同类型的数组
【发布时间】:2020-04-17 17:49:59
【问题描述】:

我有一个对象数组,我想将其写入 json 文件并稍后恢复。必须保留对象的顺序。

interface Task {}
class TaskA : Task {}
class TaskB : Task {}

var array = new Task[] {new TaskB(), new TaskA()};

如果我使用简单的JsonConvert.SerializeObject(array);,我会得到一个Objects 的列表,并且无法恢复对象的类型(我无法通过对象的字段恢复对象的类型)。

我找到了一种用它写对象类型的方法:

var serializer = new Newtonsoft.Json.JsonSerializer();
serializer.TypeNameHandling = TypeNameHandling.Objects;

我不想手动读取每个字段,我相信必须有一种方法告诉 Newtonsoft:像往常一样读取它,只需考虑对象的类型即可。它是微不足道的,我只是错过了一些东西吗?

【问题讨论】:

  • 你可以使用TypeNameHandling.Auto: newtonsoft.com/json/help/html/SerializeTypeNameHandling.htm
  • ... 我发布的那个链接?它也有一个反序列化的例子。
  • 是的,对不起,我以某种方式错过了链接......这听起来完全像我正在寻找的解决方案(为什么不将其作为答案发布?)但它仍然读取内容我的数组作为“对象”。就像,所有ChildrenTokens 都具有Object 类型。我不明白为什么。我试过TypeNameHandling.Auto,我试过TypeNameHandling.Object - 该类型是用JSON编写的,但在阅读过程中它完全被忽略了......
  • 哦,我已经把阅读从JsonConvert.DeserializeObject() 变成了JsonConvert.DeserializeObject<Task[]>(),它成功了!我希望您将您的建议作为答案发布,以便我接受它:)

标签: c# json json.net


【解决方案1】:

尝试将您的数组或列表包装在class 中。 模型定义部分:

public interface Task
{
    public string StringProperty { get; set; }
    public int IntProperty { get; set; }
}

public class TaskA : Task
{
    public string StringProperty { get; set; }
    public int IntProperty { get; set; }

    public double DoubleValue; // specific TaskA member
}

public class TaskB : Task
{
    public string StringProperty { get; set; }
    public int IntProperty { get; set; }

    public string StringValue; // specific TaskB member
}

public class MyModel
{
    public List<Task> tasks { get; set; }
}

及用法:

var data = new MyModel
{
    tasks = new List<Task>
    {
        new TaskA
        {
            IntProperty = 2,
            StringProperty = "A",
            DoubleValue = 11.1
        },
        new TaskB
        {
            IntProperty = 1,
            StringProperty = "B",
            StringValue = "Something else from B"
        }
    }
};

var settings = new JsonSerializerSettings
{
    TypeNameHandling = TypeNameHandling.Objects
};

var serializeData = JsonConvert.SerializeObject(data, Formatting.Indented, settings);

var deserializedData = JsonConvert.DeserializeObject<MyModel>(serializeData,
    new JsonSerializerSettings
    {
        TypeNameHandling = TypeNameHandling.Auto
    });

Console.WriteLine($"Count of items: {deserializedData?.tasks?.Count}");

deserializedData?.tasks?.ForEach(item =>
{
    Console.WriteLine($"String Property: {item.StringProperty}," +
                      $"Int Property: {item.IntProperty}");

    switch (item)
    {
        case TaskA taskA:
            Console.WriteLine($"taskA inner member: {taskA.DoubleValue}");
            break;
        case TaskB taskB:
            Console.WriteLine($"taskB inner member: {taskB.StringProperty}");
            break;
    }
});

【讨论】:

  • 我会试试的,但你能解释一下它应该如何解决我的问题吗?
  • 也需要getter和setter吗?
  • 对不起,我错过了,我会尽快提供完整的例子。
  • @elena 请检查是否在上面。对于我在这里的任何问题,请告诉我;)
  • 原来没有必要将任何东西包装到类中,当一个指定预期返回类型到反序列化器(在我的例子中是 Task[])和 TypeNameHandling 时,它可以很好地与集合一起使用
猜你喜欢
  • 1970-01-01
  • 2015-08-02
  • 1970-01-01
  • 1970-01-01
  • 2020-02-28
  • 1970-01-01
  • 1970-01-01
  • 2020-10-15
  • 2019-01-04
相关资源
最近更新 更多