![](/img/trans.png)
[英]Can Json.Net cast a deserialized object to the type embedded in the Json?
[英]Json.Net - How to specify deserialized type in ambiguous 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" }
};
作為內置在內存中的集合,我可以使用強制轉換根據它們的基礎類型來處理這些對象:
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";
}
或者更好,作為type屬性:
[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 :)
我能夠從Google收集到的答案中,最接近的答案可能是嘗試使用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
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.