将平面 JSON/Dictionary 映射到模型(包含子 类)
Mapping flat JSON/Dictionary to model (containing sub classes)
我想把一个扁平的json字符串变成一个模型,目标class有子classes,扁平json有所有的子class 有前缀的对象;像“{classname}。{属性}”。
{
"FirstName": "Joey",
"LastName": "Billy",
"EmploymentDetails.JobTitle": "JobTitle",
"EmploymentDetails.StartDate": "2015-01-01T00:00:00",
"ContactDetails.HouseNumberName": "10",
"ContactDetails.Road": "Road"
}
这是我的目的地class:
public class Person {
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual EmploymentDetails EmploymentDetails { get;set;}
public virtual ContactDetails ContactDetails { get;set;}
}
public class EmploymentDetails {
public string JobTitle { get; set; }
public DateTime StartDate { get; set; }
}
public class ContactDetails {
public string HouseNumberName { get; set; }
public string Road { get; set; }
}
我尝试了以下方法:
public static void main() {
var json = @"{""FirstName"": ""Joey"",""LastName"": ""Billy"",""EmploymentDetails.JobTitle"": ""JobTitle"",""EmploymentDetails.StartDate"": ""2015-01-01T00:00:00"",""ContactDetails.HouseNumberName"": ""10"",""ContactDetails.Road"": ""Road"",}";
//try using AutoMapper
Mapper.CreateMap<string,Person>();
var personModel = Mapper.Map<Person>(json);
//just returns null values
//try using Newtonsoft
personModel = Newtonsoft.Json.JsonConvert.DeserializeObject<Person>(json);
//fills values but obviously returns no nested data
}
我知道 Automapper 有 RecognizePrefix 和 RecognizeDestinationPrefix,但 AutoMapper 似乎只关心它是否在原始对象中,而不关心子对象 class。
可能我可以把我的 JSON 字符串变成一个字典,但即便如此我也不知道如何将它映射到具有子 classes 的模型。
我曾希望我可以拥有无限数量的子 classes 并且 JSON 字符串可以将平面 JSON 模型映射到模型。
您可以制作一个 JsonConverter
that does this in a generic way, using a ContractResolver
来分组和填充正在反序列化的 class 或其包含的 class 中的属性(视情况而定)。
你没有要求序列化,只要求反序列化,所以这就是它的作用:
public class JsonFlatteningConverter : JsonConverter
{
readonly IContractResolver resolver;
public JsonFlatteningConverter(IContractResolver resolver)
{
if (resolver == null)
throw new ArgumentNullException();
this.resolver = resolver;
}
public override bool CanConvert(Type objectType)
{
return resolver.ResolveContract(objectType) is JsonObjectContract;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
JObject jObject = JObject.Load(reader);
var contract = (JsonObjectContract)resolver.ResolveContract(objectType); // Throw an InvalidCastException if this object does not map to a JObject.
existingValue = existingValue ?? contract.DefaultCreator();
if (jObject.Count == 0)
return existingValue;
var groups = jObject.Properties().GroupBy(p => p.Name.Contains('.') ? p.Name.Split('.').FirstOrDefault() : null).ToArray();
foreach (var group in groups)
{
if (string.IsNullOrEmpty(group.Key))
{
var subObj = new JObject(group);
using (var subReader = subObj.CreateReader())
serializer.Populate(subReader, existingValue);
}
else
{
var jsonProperty = contract.Properties[group.Key];
if (jsonProperty == null || !jsonProperty.Writable)
continue;
if (jsonProperty != null)
{
var subObj = new JObject(group.Select(p => new JProperty(p.Name.Substring(group.Key.Length + 1), p.Value)));
using (var subReader = subObj.CreateReader())
{
var propertyValue = serializer.Deserialize(subReader, jsonProperty.PropertyType);
jsonProperty.ValueProvider.SetValue(existingValue, propertyValue);
}
}
}
}
return existingValue;
}
public override bool CanWrite { get { return false; } }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
然后这样使用:
var resolver = new DefaultContractResolver();
var settings = new JsonSerializerSettings { ContractResolver = resolver, Converters = new JsonConverter[] { new JsonFlatteningConverter(resolver) } };
var person = JsonConvert.DeserializeObject<Person>(json, settings);
原型fiddle.
我想把一个扁平的json字符串变成一个模型,目标class有子classes,扁平json有所有的子class 有前缀的对象;像“{classname}。{属性}”。
{
"FirstName": "Joey",
"LastName": "Billy",
"EmploymentDetails.JobTitle": "JobTitle",
"EmploymentDetails.StartDate": "2015-01-01T00:00:00",
"ContactDetails.HouseNumberName": "10",
"ContactDetails.Road": "Road"
}
这是我的目的地class:
public class Person {
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual EmploymentDetails EmploymentDetails { get;set;}
public virtual ContactDetails ContactDetails { get;set;}
}
public class EmploymentDetails {
public string JobTitle { get; set; }
public DateTime StartDate { get; set; }
}
public class ContactDetails {
public string HouseNumberName { get; set; }
public string Road { get; set; }
}
我尝试了以下方法:
public static void main() {
var json = @"{""FirstName"": ""Joey"",""LastName"": ""Billy"",""EmploymentDetails.JobTitle"": ""JobTitle"",""EmploymentDetails.StartDate"": ""2015-01-01T00:00:00"",""ContactDetails.HouseNumberName"": ""10"",""ContactDetails.Road"": ""Road"",}";
//try using AutoMapper
Mapper.CreateMap<string,Person>();
var personModel = Mapper.Map<Person>(json);
//just returns null values
//try using Newtonsoft
personModel = Newtonsoft.Json.JsonConvert.DeserializeObject<Person>(json);
//fills values but obviously returns no nested data
}
我知道 Automapper 有 RecognizePrefix 和 RecognizeDestinationPrefix,但 AutoMapper 似乎只关心它是否在原始对象中,而不关心子对象 class。
可能我可以把我的 JSON 字符串变成一个字典,但即便如此我也不知道如何将它映射到具有子 classes 的模型。
我曾希望我可以拥有无限数量的子 classes 并且 JSON 字符串可以将平面 JSON 模型映射到模型。
您可以制作一个 JsonConverter
that does this in a generic way, using a ContractResolver
来分组和填充正在反序列化的 class 或其包含的 class 中的属性(视情况而定)。
你没有要求序列化,只要求反序列化,所以这就是它的作用:
public class JsonFlatteningConverter : JsonConverter
{
readonly IContractResolver resolver;
public JsonFlatteningConverter(IContractResolver resolver)
{
if (resolver == null)
throw new ArgumentNullException();
this.resolver = resolver;
}
public override bool CanConvert(Type objectType)
{
return resolver.ResolveContract(objectType) is JsonObjectContract;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
JObject jObject = JObject.Load(reader);
var contract = (JsonObjectContract)resolver.ResolveContract(objectType); // Throw an InvalidCastException if this object does not map to a JObject.
existingValue = existingValue ?? contract.DefaultCreator();
if (jObject.Count == 0)
return existingValue;
var groups = jObject.Properties().GroupBy(p => p.Name.Contains('.') ? p.Name.Split('.').FirstOrDefault() : null).ToArray();
foreach (var group in groups)
{
if (string.IsNullOrEmpty(group.Key))
{
var subObj = new JObject(group);
using (var subReader = subObj.CreateReader())
serializer.Populate(subReader, existingValue);
}
else
{
var jsonProperty = contract.Properties[group.Key];
if (jsonProperty == null || !jsonProperty.Writable)
continue;
if (jsonProperty != null)
{
var subObj = new JObject(group.Select(p => new JProperty(p.Name.Substring(group.Key.Length + 1), p.Value)));
using (var subReader = subObj.CreateReader())
{
var propertyValue = serializer.Deserialize(subReader, jsonProperty.PropertyType);
jsonProperty.ValueProvider.SetValue(existingValue, propertyValue);
}
}
}
}
return existingValue;
}
public override bool CanWrite { get { return false; } }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
然后这样使用:
var resolver = new DefaultContractResolver();
var settings = new JsonSerializerSettings { ContractResolver = resolver, Converters = new JsonConverter[] { new JsonFlatteningConverter(resolver) } };
var person = JsonConvert.DeserializeObject<Person>(json, settings);
原型fiddle.