Json.Net-如何在不明确的Json中指定反序列化类型



到目前为止,我似乎找不到一个好的答案,但我承认,也许我不够聪明,不知道要搜索的正确关键字。好了。

假设我有一个包含混合对象类型的集合:

var wishList = new List<WishListItem>
{
new Car { Price = 78000, Make = "Tesla", Model = "S", Name = "Tesla Model S" },
new Computer { Manufacturer = "Microsoft", Name = "Surface Pro 6", Price = 2000 },
new PlatitudeIdea { Name = "World peace" }
};

作为一个内置在内存中的集合,我可以使用casting根据它们的底层类型来处理这些对象:

foreach (var wishListItem in wishList)
{
if (wishListItem is PlatitudeIdea platitude)
{
Console.WriteLine($"{platitude.Name} is a hopeless dream");
}
else if (wishListItem is IPriceable priceThing)
{
Console.WriteLine($"At {priceThing.Price}, {priceThing.Name} is way out of my budget");
}
else
{
Console.WriteLine($"I want a {wishListItem.Name}");
}
}

如果我将它序列化为JSON数组,一切看起来都很好。。。

[
{ "Price": 78000, "Make": "Tesla", "Model": "S", "Name": "Tesla Model S" },
{ "Manufacturer ": "Microsoft", "Name": "Surface Pro 6", "Price": 2000 },
{ "Name": "World peace" }
]

但是当我解析JSON时,解析器显然无法准确判断每个元素最初是哪种类型,所以它只是试图将它们解析为List的泛型参数(WishListItem)中声明的最低级别类型,正如我所期望的:

parsedWishList[0] is WishListitem // returns true :)
parsedWishList[0] is Car // returns false :(

这是有意义的,并且您可以在任何时候将被序列化的成员声明为超类型或接口时获得这种行为。我希望能够做的是向我的特定类添加一个特殊的属性,指示正在序列化的对象的类型:

public class Car : WishListItem, IPriceable 
{
public override string @type => "Car";
}

或者更好的是,作为一种类型属性:

[JsonSerializedType("Car")]
public class Car : WishListItem, IPriceable 
{
// ...
}

然后,只要类型声明不明确,就会将其输出到JSON。。。

[
{ "type": "Car", "Price": 78000, "Make": "Tesla", "Model": "S" },
{ "type": "Computer", "Manufacturer ": "Microsoft", "Name": "Surface Pro 6", "Price": 2000 },
{ "type": "Platitude", "Value": "World peace" }
]

解析器会将该对象反序列化为以下类型:

parsedWishList[0] is Car // returns true :)

我能从谷歌那里得到的最接近答案可能是尝试使用CustomCreationConverter,看看这是否会有所帮助。但我需要一个非常通用的答案,我可以写一次,让它处理任意类型。

有指针吗?

听起来您正在寻找TypeNameHandling设置。此设置将导致Json.Net将类型信息写入Json,以便将其反序列化回其原始类型。

如果需要自定义类型名称,可以使用自定义SerializationBinder类。

以下是一个基于文档中所示KnownTypesBinder示例的往返演示:

using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
namespace SO54465235
{
public class Program
{
public static void Main(string[] args)
{
var wishList = new List<WishListItem>
{
new Car { Price = 78000, Make = "Tesla", Model = "S", Name = "Tesla Model S" },
new Computer { Manufacturer = "Microsoft", Name = "Surface Pro 6", Price = 2000 },
new Platitude { Name = "World peace" }
};
KnownTypesBinder knownTypesBinder = new KnownTypesBinder
{
KnownTypes = new List<Type> { typeof(Car), typeof(Computer), typeof(Platitude) }
};
string json = JsonConvert.SerializeObject(wishList, Formatting.Indented, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Objects,
SerializationBinder = knownTypesBinder
});
Console.WriteLine(json);
Console.WriteLine();
List<WishListItem> items = JsonConvert.DeserializeObject<List<WishListItem>>(json, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Objects,
SerializationBinder = knownTypesBinder
});
foreach (var wishListItem in wishList)
{
if (wishListItem is Platitude platitude)
{
Console.WriteLine($"{platitude.Name} is a hopeless dream");
}
else if (wishListItem is IPriceable priceThing)
{
Console.WriteLine($"At {priceThing.Price}, {priceThing.Name} is way out of my budget");
}
else
{
Console.WriteLine($"I want a {wishListItem.Name}");
}
}
}
}
public class KnownTypesBinder : ISerializationBinder
{
public IList<Type> KnownTypes { get; set; }
public Type BindToType(string assemblyName, string typeName)
{
return KnownTypes.SingleOrDefault(t => t.Name == typeName);
}
public void BindToName(Type serializedType, out string assemblyName, out string typeName)
{
assemblyName = null;
typeName = serializedType.Name;
}
}
class WishListItem
{
public string Name { get; set; }
}
interface IPriceable
{
int Price { get; set; }
string Name { get; set; }
}
class Car : WishListItem, IPriceable
{
public string Make { get; set; }
public string Model { get; set; }
public int Price { get; set; }
}
class Computer : WishListItem, IPriceable
{
public string Manufacturer { get; set; }
public int Price { get; set; }
}
class Platitude : WishListItem
{
}
}

输出:

[
{
"$type": "Car",
"Make": "Tesla",
"Model": "S",
"Price": 78000,
"Name": "Tesla Model S"
},
{
"$type": "Computer",
"Manufacturer": "Microsoft",
"Price": 2000,
"Name": "Surface Pro 6"
},
{
"$type": "Platitude",
"Name": "World peace"
}
]
At 78000, Tesla Model S is way out of my budget
At 2000, Surface Pro 6 is way out of my budget
World peace is a hopeless dream

最新更新