Json数组到C#中的自定义对象映射 [英] Json Array to Custom Object Mapping in C#
问题描述
我有这个JSON数组作为输入.
I have this JSON Array as input.
[
{
"FirstName": "Test1",
"LastName": "Test2",
"Address": "London, GB",
"Error": "Something's gone wrong"
},
{
"FirstName": "Test3",
"LastName": "Test4",
"Address": "NewYork, US",
"Error": "Something's gone wrong"
},
{
"DisplayName": "ContactNumber",
"Value": "01234 123 123"
}
]
我想在C#中构建这样的JSON对象
I want to build a JSON Object like this in C#
[
"pages":{
"FirstName": "Test1",
"LastName": "Test2",
"Address": "London, GB",
"Error": "Something's gone wrong"
},
{
"FirstName": "Test3",
"LastName": "Test4",
"Address": "NewYork, US",
"Error": "Something's gone wrong"
},
"labels": {
"DisplayName": "ContactNumber",
"Value": "01234 123 123"
}
}
]
我已经使用上面的输出属性创建了一个Model,但是当我将该对象反序列化到那个Model中时,它们没有被映射.所有值都返回null.
I've created a Model with above output properties but they are not getting mapped when I deserialize the object into that Model. All values are returning null.
var deserializedData = JsonConvert.DeserializeObject<List<Config>>(serializedData);
我检查时收到的回复是
[
{
"pages": null,
"labels": null
},
{
"pages": null,
"labels": null
}
]
任何人都可以帮助我在 C#中为此格式构建自定义模型.
Can anyone help me build the Custom Model for this format I want in C#.
谢谢.
推荐答案
您拥有的是一个多态JSON数组,其中包含各种类型的对象,可以通过存在特定属性(例如"DisplayName"
vs "FirstName"
)来区分. ).您想要做的是将其反序列化为c#模型,其中将每个可能的数组项添加到模型的collection-valued属性中,在该属性中,collection属性被选择为具有正确的项类型.
What you have is a polymorphic JSON array containing a variety of types of objects, distinguishable by the presence of specific properties ("DisplayName"
vs "FirstName"
, e.g.). What you would like to do is to deserialize that into a c# model in which each possible array item gets added to a collection-valued property of your model, where the collection property is chosen to have the correct item type.
这可以通过使用自定义JsonConverter
来完成. .由于问题陈述是通用的,因此我将使转换器通用.硬编码转换器将需要更少的代码.
This can be accomplished by using a custom JsonConverter
. Since the problem statement is generic I'm going to make the converter be generic. A hardcoded converter would require less code.
首先,如下定义所需的模型:
First, define your desired model as follows:
public class Page
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Address { get; set; }
public string Error { get; set; }
}
public class Label
{
public string DisplayName { get; set; }
public string Value { get; set; }
}
public class RootObject
{
public List<Page> pages { get; set; }
public List<Label> labels { get; set; }
}
接下来定义以下转换器:
Next define the following converter:
public class PolymorphicArrayToObjectConverter<TObject> : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(TObject).IsAssignableFrom(objectType);
}
public override bool CanWrite { get { return false; } }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
static JsonObjectContract FindContract(JObject obj, IEnumerable<Type> derivedTypes, JsonSerializer serializer)
{
List<JsonObjectContract> bestContracts = new List<JsonObjectContract>();
foreach (var type in derivedTypes)
{
if (type.IsAbstract)
continue;
var contract = serializer.ContractResolver.ResolveContract(type) as JsonObjectContract;
if (contract == null)
continue;
if (obj.Properties().Select(p => p.Name).Any(n => contract.Properties.GetClosestMatchProperty(n) == null))
continue;
if (bestContracts.Count == 0 || bestContracts[0].Properties.Count > contract.Properties.Count)
{
bestContracts.Clear();
bestContracts.Add(contract);
}
else if (contract.Properties.Count == bestContracts[0].Properties.Count)
{
bestContracts.Add(contract);
}
}
return bestContracts.Single();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
else if (reader.TokenType != JsonToken.StartArray)
throw new InvalidOperationException("JSON token is not an array at path: " + reader.Path);
var contract = (JsonObjectContract)serializer.ContractResolver.ResolveContract(objectType);
existingValue = existingValue ?? contract.DefaultCreator();
var lookup = contract
.Properties
.Select(p => new { Property = p, PropertyContract = serializer.ContractResolver.ResolveContract(p.PropertyType) as JsonArrayContract })
.Where(i => i.PropertyContract != null)
.ToDictionary(i => i.PropertyContract.CollectionItemType);
var types = lookup.Select(i => i.Key).ToList();
while (reader.Read())
{
switch (reader.TokenType)
{
case JsonToken.Comment:
break;
case JsonToken.EndArray:
return existingValue;
default:
{
var itemObj = JObject.Load(reader);
var itemContract = FindContract(itemObj, types, serializer);
if (itemContract == null)
continue;
var item = serializer.Deserialize(itemObj.CreateReader(), itemContract.UnderlyingType);
var propertyData = lookup[itemContract.UnderlyingType];
var collection = propertyData.Property.ValueProvider.GetValue(existingValue);
if (collection == null)
{
collection = propertyData.PropertyContract.DefaultCreator();
propertyData.Property.ValueProvider.SetValue(existingValue, collection);
}
collection.GetType().GetMethod("Add").Invoke(collection, new [] { item });
}
break;
}
}
// Should not come here.
throw new JsonSerializationException("Unclosed array at path: " + reader.Path);
}
}
然后,按如下所示反序列化并重新序列化RootObject
集合:
Then, deserialize and re-serialize your RootObject
collection as follows:
var settings = new JsonSerializerSettings
{
Converters = { new PolymorphicArrayToObjectConverter<RootObject>() },
};
var root = new List<RootObject> { JsonConvert.DeserializeObject<RootObject>(jsonString, settings) };
var outputJson = JsonConvert.SerializeObject(root, Formatting.Indented);
因此,将生成以下JSON:
As a result, the following JSON will be generated:
[
{
"pages": [
{
"FirstName": "Test1",
"LastName": "Test2",
"Address": "London, GB",
"Error": "Something's gone wrong"
},
{
"FirstName": "Test3",
"LastName": "Test4",
"Address": "NewYork, US",
"Error": "Something's gone wrong"
}
],
"labels": [
{
"DisplayName": "ContactNumber",
"Value": "01234 123 123"
}
]
}
]
样本小提琴.
注意-自动推断数组项类型的代码改编自此答案.
Note - code to automatically infer the array item type was adapted from this answer.
这篇关于Json数组到C#中的自定义对象映射的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!