gpt4 book ai didi

c# - C# 中的 Json 数组到自定义对象的映射

转载 作者:太空宇宙 更新时间:2023-11-03 14:58:30 25 4
gpt4 key购买 nike

我有这个 JSON 数组作为输入。

[
{
"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 对象

[
"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"
}
}
]

我已经创建了一个具有上述输出属性的模型,但是当我将对象反序列化到该模型时,它们没有被映射。所有值都返回 null。

var deserializedData = JsonConvert.DeserializeObject<List<Config>>(serializedData);

我检查时收到的响应是

[
{
"pages": null,
"labels": null
},
{
"pages": null,
"labels": null
}
]

谁能帮我在 C# 中为我想要的这种格式构建自定义模型

提前致谢。

最佳答案

您拥有的是一个包含各种类型对象的多态 JSON 数组,可通过特定属性("DisplayName""FirstName",例如).您想要做的是将其反序列化为一个 C# 模型,其中每个可能的数组项都被添加到模型的一个集合值属性中,其中集合属性被选择为具有正确的项类型。

这可以通过使用 custom JsonConverter 来完成.由于问题陈述是通用的,我打算让转换器通用。硬编码转换器需要的代码更少。

首先,定义你想要的模型如下:

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; }
}

接下来定义如下转换器:

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 集合,如下所示:

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:

[
{
"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"
}
]
}
]

样本 fiddle .

注意 - 自动推断数组项类型的代码改编自 this answer .

关于c# - C# 中的 Json 数组到自定义对象的映射,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47578150/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com