将平面JSON / Dictionary映射到模型(包含子类)

我想将一个扁平的json字符串转换为一个模型,目标类具有子类,而扁平的json具有带前缀的所有子类对象; 比如“{classname}。{property}”。

{ "FirstName": "Joey", "LastName": "Billy", "EmploymentDetails.JobTitle": "JobTitle", "EmploymentDetails.StartDate": "2015-01-01T00:00:00", "ContactDetails.HouseNumberName": "10", "ContactDetails.Road": "Road" } 

这是我的目的地类:

 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(); var personModel = Mapper.Map(json); //just returns null values //try using Newtonsoft personModel = Newtonsoft.Json.JsonConvert.DeserializeObject(json); //fills values but obviously returns no nested data } 

我知道Automapper有RecognizePrefix和RecognizeDestinationPrefix,但AutoMapper似乎只关心它是否在orignal对象中,而不是子类。

可能我可以使用我的JSON字符串并使其成为一个字典,但即便如此我也不知道如何将它映射到具有子类的模型。

希望我可以拥有无​​限量的子类,而JSON字符串可以将平面JSON模型映射到模型。

您可以创建一个以通用方式执行此操作的JsonConverter ,使用ContractResolver根据需要对要反序列化的类或其包含的类中的属性进行分组和填充。

你没有要求序列化,只要求反序列化,这就是它的作用:

 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(json, settings); 

原型小提琴 。