如何使用Json.net反序列化对象列表而不事先知道它们的类型?

时间:2016-05-04 17:53:15

标签: json.net

我有一个包含多行json的文本文件,每行代表几种类型之一。例如,文件中的第一行可能表示序列化的Foo,而第二行表示序列化的Bar,依此类推。

问题是如何反序列化这些行? (反序列化要求您指定要使用的类型,但我事先并不知道每行代表哪种类型)

首先,我尝试在序列化程序上使用TypeNameHandling = TypeNameHandling.Objects,它将字段名$type嵌入到序列化的json中。这看起来很有希望我以为我可以将每一行作为动态对象读取,如下所示:

var o = JsonConvert.DeserializeObject<dynamic>(line_from_file);

然后提取出$type字段,然后使用它来反序列化具体类,如下所示:

var concrete_object = JsonConvert.DeserializeObject<(type indicated in $type)>(line_from_file);

$type字段名称似乎无法在动态反序列化返回的o对象中访问。

我想我可以对每一行进行文本解析,寻找$type,但这看起来也很混乱。

1 个答案:

答案 0 :(得分:3)

序列化时使用TypeNameHandling = TypeNameHandling.Objects的重点是,您不必进行任何特殊处理以反序列化回特定类型。只要在反序列化时也指定TypeNameHandling = TypeNameHandling.Objects,反序列化器将读取嵌入的$type属性并为您实例化/填充正确的对象。 (请注意,DeserializeObject为此目的具有非泛型重载,需要指定Type参数;这可能是您混淆的原因。)

下面是一个往返演示程序来说明这一点。它将创建几个测试对象,按照问题中的描述将它们序列化为临时JSON文件,然后重新读入文件,将每行反序列化回一个对象并将其类型和内容转储到控制台。

class Program
{
    static void Main(string[] args)
    {
        Foo foo = new Foo { Id = 1, Name = "foo" };
        Bar bar = new Bar { Length = 2.3, Width = 1.6 };

        JsonSerializerSettings settings = new JsonSerializerSettings
        {
            TypeNameHandling = TypeNameHandling.Objects,
            Formatting = Formatting.None      // ensure no line breaks in the JSON
        };

        string fileName = @"C:\temp\Q37034748.json";

        // Write out sample file, one object per line
        using (StreamWriter sw = new StreamWriter(fileName, false, Encoding.UTF8))
        {
            sw.WriteLine(JsonConvert.SerializeObject(foo, settings));
            sw.WriteLine(JsonConvert.SerializeObject(bar, settings));
        }

        // Now read the file, deserializing each line to an object and dumping it out
        using (StreamReader sr = new StreamReader(fileName, Encoding.UTF8))
        {
            while (!sr.EndOfStream)
            {
                object obj = JsonConvert.DeserializeObject(sr.ReadLine(), settings);
                Dump(obj);
            }
        }
    }

    private static void Dump(object obj)
    {
        if (obj != null) 
        {
            Type type = obj.GetType();
            Console.WriteLine(type.FullName);
            foreach (PropertyInfo prop in type.GetProperties())
            {
                Console.WriteLine(prop.Name + ": " + prop.GetValue(obj));
            }
        }
        else
        {
            Console.WriteLine("null");
        }
        Console.WriteLine();
    }

}

class Foo
{
    public int Id { get; set; }
    public string Name { get; set; }
}

class Bar
{
    public double Length { get; set; }
    public double Width { get; set; }
}

JSON文件看起来与此类似(您的命名空间/程序集名称可能不同):

{"$type":"JsonTest.Foo, JsonTest","Id":1,"Name":"foo"}
{"$type":"JsonTest.Bar, JsonTest","Length":2.3,"Width":1.6}

控制台输出如下所示:

JsonTest.Foo
Id: 1
Name: foo

JsonTest.Bar
Length: 2.3
Width: 1.6