考虑以下JSON:
{
"Foo": "Whatever",
"Bar": [
{ "Name": "Enrico", "Age": 33, "Country": "Italy" }, { "Type": "Video", "Year": 2004 },
{ "Name": "Sam", "Age": 18, "Country": "USA" }, { "Type": "Book", "Year": 1980 }
]
}
注意,Items
数组是一个混合内容数组,它包含具有不同形状的对象。
这些形状之一可以使用以下类来描述:
class Person
{
public string Name { get; set; }
public int Age { get; set; }
public string Country { get; set; }
}
相反,可以使用以下C#类来描述另一个形状:
class Item
{
public string Type { get; set; }
public int Year { get; set; }
}
我想通过使用C#或newtonsoft.json将这个JSON反序列化为一个System.Text.Json类。在这两种情况下,我都需要一个用于反序列化的类,但我不知道如何处理Bar
数组。
class ClassToDeserialize
{
public string Foo { get; set; }
public List<what should I put here ???> Bar { get; set; }
}
如何反序列化这个JSON?
对于熟悉类型记录的人,我需要类似于union类型的东西(例如:将Bar
属性定义为List<Person | Item>
),但根据我的知识,C#中不支持联合类型。
发布于 2021-03-24 08:39:29
我将为列表项定义一个公共接口IBar
,然后让类实现这个接口。IBar
可能只是一个空接口,也可以选择将Type
属性放入其中并向Person
类添加一个合成Type
属性以匹配:
interface IBar
{
string Type { get; }
}
class Person : IBar
{
public string Type => "Person";
public string Name { get; set; }
public int Age { get; set; }
public string Country { get; set; }
}
class Item : IBar
{
public string Type { get; set; }
public int Year { get; set; }
}
class ClassToDeserialize
{
public string Foo { get; set; }
public List<IBar> Bar { get; set; }
}
要从JSON填充类,可以使用以下简单的JsonConverter
:
public class BarConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(IBar).IsAssignableFrom(objectType);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JObject jo = JObject.Load(reader);
// If the "age" property is present in the JSON, it's a person, otherwise it's an item
IBar bar;
if (jo["age"] != null)
{
bar = new Person();
}
else
{
bar = new Item();
}
serializer.Populate(jo.CreateReader(), bar);
return bar;
}
public override bool CanWrite => false;
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
最后一块拼图是用一个IBar
属性装饰[JsonConverter]
接口,告诉序列化程序在处理IBar
时使用转换器。
[JsonConverter(typeof(BarConverter))]
interface IBar
{
string Type { get; }
}
然后,您可以像通常那样反序列化:
var root = JsonConvert.DeserializeObject<ClassToDeserialize>(json);
下面是一个工作演示:https://dotnetfiddle.net/ENLgVx
发布于 2021-03-23 16:02:16
创建一个具有两个属性但具有可空选项的类。然后,可以使用各个类的属性创建两个接口。然后,一旦您收到它为一个列表,您可以组织成两个不同的列表类型IYourInterface
发布于 2021-03-23 16:56:52
这是可行的,但它比我预期的要复杂一些。首先创建一些接口:
public interface IPerson
{
string Name { get; }
int Age { get; }
string Country { get; }
}
public interface IItem
{
string Type { get; }
int Year { get; }
}
}
我们会用它们来代表你的人和物品。
然后创建另一个类:
public class JsonDynamicList
{
private const string JsonData =
@"{
'Foo': 'Whatever',
'Bar': [
{ 'Name': 'Enrico', 'Age': 33, 'Country': 'Italy' }, { 'Type': 'Video', 'Year': 2004 },
{ 'Name': 'Sam', 'Age': 18, 'Country': 'USA' }, { 'Type': 'Book', 'Year': 1980 }
]
}";
public string Foo { get; set; }
public dynamic[] Bar { get; set; }
}
它与您创建的类相匹配,但我使用的是dynamic
数组,而不是List<something>
。还请注意,通过更改引号使您的JSON更加友好--它是同一个C#。
我们会继续增加那个班的成员。
首先,我创建了两个实现IPerson
和IItem
的私有类。它们位于JsonDynamicList
类中:
private class PersonImpl :IPerson
{
private readonly dynamic _person;
public PersonImpl(dynamic person)
{
_person = person;
}
public IPerson AsPerson()
{
if (!IsPerson(_person))
{
return null;
}
//otherwise
Name = _person.Name;
Age = _person.Age;
Country = _person.Country;
return this;
}
public string Name { get; private set; } = default;
public int Age { get; private set; } = default;
public string Country { get; private set; } = default;
}
private class ItemImpl : IItem
{
private readonly dynamic _item;
public ItemImpl(dynamic item)
{
_item = item;
}
public IItem AsItem()
{
if (!IsItem(_item))
{
return null;
}
//otherwise
Type = _item.Type;
Year = _item.Year;
return this;
}
public string Type { get; private set; } = default;
public int Year { get; private set; } = default;
}
我使用了一些Newtonsoft魔术来实现JsonDynamicList
的下两个成员。他们可以决定一个项目是IItem
还是IPerson
public static bool IsPerson(dynamic person)
{
var jObjectPerson = ((JObject) person).ToObject<Dictionary<string, object>>();
return jObjectPerson?.ContainsKey("Age") ?? false;
}
public static bool IsItem(dynamic item)
{
var jObjectItem = ((JObject)item).ToObject<Dictionary<string, object>>();
return jObjectItem?.ContainsKey("Year") ?? false;
}
如果有人知道一个更好的方法来判断一个动态是否有一个特定的成员,我很想知道。
然后,我创建了一种方法来将数组中的一个项转换为输入项(嗯,它实际上不是一个强制转换,但您可以这样想)。我用了前两个,我想我要用第二个。
public static IPerson AsPerson(dynamic person)
{
var personImpl = new PersonImpl(person);
return personImpl.AsPerson();
}
public static IItem AsItem(dynamic item)
{
var itemImpl = new ItemImpl(item);
return itemImpl.AsItem();
}
public IItem AsItem(int index)
{
if (index < 0 || index >= Bar.Length)
{
throw new IndexOutOfRangeException();
}
return AsItem(Bar[index]);
}
public IPerson AsPerson(int index)
{
if (index < 0 || index >= Bar.Length)
{
throw new IndexOutOfRangeException();
}
return AsPerson(Bar[index]);
}
一些便于测试的工作人员方法:
public static string ItemToString(IItem item)
{
return $"Type: {item.Type} - Year: {item.Year}";
}
public static string PersonToString(IPerson person)
{
return $"Name: {person.Name} - Age: {person.Age} - Country: {person.Country}";
}
最后,一些测试代码:
var data = JsonConvert.DeserializeObject<JsonDynamicList>(JsonData);
Debug.WriteLine($"Foo: {data.Foo}");
foreach (dynamic obj in data.Bar)
{
if (IsItem(obj))
{
string itemString = ItemToString(AsItem(obj));
Debug.WriteLine(itemString);
}
else
{
string personString = PersonToString(AsPerson(obj));
Debug.WriteLine(personString);
}
}
这导致:
Foo: Whatever
Name: Enrico - Age: 33 - Country: Italy
Type: Video - Year: 2004
Name: Sam - Age: 18 - Country: USA
Type: Book - Year: 1980
https://stackoverflow.com/questions/66772647
复制相似问题