Десериализация JSON, вложенные свойства объекта должны быть в родительском объекте. C#
У меня есть следующий JSON, который я пишу объектную модель для десериализации в:
{
"company_webhooks": [
{
"company_webhook": {
"id": 42,
"url": "https://keeptruckin.com/callbacktest/842b02",
"secret": "fe8b75de0a4e5898f0011faeb8c93654",
"format": "json",
"actions": [
"vehicle_location_received",
"vehicle_location_updated"
],
"enabled": false
}
},
{
"company_webhook": {
"id": 43,
"url": "https://keeptruckin.com/callbacktest/a6a783",
"secret": "66a7368063cb21887f546c7af91be59c",
"format": "json",
"actions": [
"vehicle_location_received",
"vehicle_location_updated"
],
"enabled": false
}
},
{
"company_webhook": {
"id": 44,
"url": "https://keeptruckin.com/callbacktest/53a52c",
"secret": "4451dc96513b3a67107466dd2c4d9589",
"format": "json",
"actions": [
"vehicle_location_received",
"vehicle_location_updated"
],
"enabled": false
}
},
{
"company_webhook": {
"id": 45,
"url": "https://keeptruckin.com/callbacktest/6fb337",
"secret": "4177fbd88c30faaee03a4362648bd663",
"format": "json",
"actions": [
"vehicle_location_received",
"vehicle_location_updated"
],
"enabled": false
}
},
{
"company_webhook": {
"id": 46,
"url": "https://keeptruckin.com/callbacktest/8cd6da",
"secret": "6e41817a048b009435e5102fca17db55",
"format": "json",
"actions": [
"vehicle_location_received",
"vehicle_location_updated"
],
"enabled": false
}
}
],
"pagination": {
"per_page": 25,
"page_no": 1,
"total": 5
}
}
Вот что у меня есть:
[DataContract]
public class KeepTruckinResponse
{
[DataMember(Name = "company_webhooks", EmitDefaultValue = false)]
public KeepTruckinCompanyWebHook[] WebHooks { get; set; }
[DataMember(Name = "pagination", EmitDefaultValue = false)]
public KeepTruckinPagination Pagination { get; set; }
public string RawJSON { get; set; }
}
[DataContract]
public class KeepTruckinPagination
{
[DataMember(Name = "per_page", EmitDefaultValue = false)]
public int PerPage { get; set; }
[DataMember(Name = "page_no", EmitDefaultValue = false)]
public int PageNumber { get; set; }
[DataMember(Name = "total", EmitDefaultValue = false)]
public int Total { get; set; }
}
[DataContract(Name = "company_webhook")]
public class KeepTruckinCompanyWebHook
{
[DataMember(Name = "id", EmitDefaultValue = false)]
public int Id { get; set; }
[DataMember(Name = "url", EmitDefaultValue = false)]
public string Url { get; set; }
}
Очевидно, что при десериализации JSON я не получаю KeepTruckinCompanyWebHook
свойства, потому что способ отправки коллекции "вложенный". Мне почти нужно создать еще один объект внутри KeepTruckinCompanyWebHook
со свойствами. Но я бы хотел сохранить мою объектную модель такой, какая она есть. Возможно ли это с.NET сериализатором?
Мы используем DataContractJsonSerializer
вот так:
var ser = new DataContractJsonSerializer(typeof(KeepTruckinResponse));
response = ser.ReadObject(ms) as KeepTruckinResponse;
На данный момент мы не хотим использовать NewtonSoft.Json
1 ответ
Да, это возможно, но для этого вам понадобится специальный код.
Это немного уродливо, но вы можете создать кастом IDataContractSurrogate
класс для десериализации каждого объекта JSON внутри company_webhooks
массив в Dictionary<string, Dictionary<string, object>>
и затем скопируйте значения из вложенной словарной структуры в экземпляр вашего KeepTruckinCompanyWebHook
учебный класс. Вот код, который вам понадобится для суррогата:
class MyDataContractSurrogate : IDataContractSurrogate
{
public Type GetDataContractType(Type type)
{
if (type == typeof(KeepTruckinCompanyWebHook))
{
return typeof(Dictionary<string, Dictionary<string, object>>);
}
return type;
}
public object GetDeserializedObject(object obj, Type targetType)
{
if (obj.GetType() == typeof(Dictionary<string, Dictionary<string, object>>) &&
targetType == typeof(KeepTruckinCompanyWebHook))
{
var webHook = new KeepTruckinCompanyWebHook();
var outerDict = (Dictionary<string, Dictionary<string, object>>)obj;
var innerDict = outerDict["company_webhook"];
foreach (PropertyInfo prop in GetDataMemberProperties(typeof(KeepTruckinCompanyWebHook)))
{
DataMemberAttribute att = prop.GetCustomAttribute<DataMemberAttribute>();
object value;
if (innerDict.TryGetValue(att.Name, out value))
{
prop.SetValue(webHook, value);
}
}
return webHook;
}
return obj;
}
public object GetObjectToSerialize(object obj, Type targetType)
{
if (obj.GetType() == typeof(KeepTruckinCompanyWebHook) &&
targetType == typeof(Dictionary<string, Dictionary<string, object>>))
{
var webHook = (KeepTruckinCompanyWebHook)obj;
var outerDict = new Dictionary<string, Dictionary<string, object>>();
var innerDict = new Dictionary<string, object>();
outerDict.Add("company_webhook", innerDict);
foreach (PropertyInfo prop in GetDataMemberProperties(typeof(KeepTruckinCompanyWebHook)))
{
DataMemberAttribute att = prop.GetCustomAttribute<DataMemberAttribute>();
innerDict.Add(att.Name, prop.GetValue(webHook));
}
return outerDict;
}
return obj;
}
private IEnumerable<PropertyInfo> GetDataMemberProperties(Type type)
{
return type.GetProperties().Where(p => p.CanRead && p.CanWrite && p.GetCustomAttribute<DataMemberAttribute>() != null);
}
// ------- The rest of these methods do not need to be implemented -------
public object GetCustomDataToExport(Type clrType, Type dataContractType)
{
throw new NotImplementedException();
}
public object GetCustomDataToExport(MemberInfo memberInfo, Type dataContractType)
{
throw new NotImplementedException();
}
public void GetKnownCustomDataTypes(System.Collections.ObjectModel.Collection<Type> customDataTypes)
{
throw new NotImplementedException();
}
public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData)
{
throw new NotImplementedException();
}
public System.CodeDom.CodeTypeDeclaration ProcessImportedType(System.CodeDom.CodeTypeDeclaration typeDeclaration, System.CodeDom.CodeCompileUnit compileUnit)
{
throw new NotImplementedException();
}
}
Чтобы использовать суррогат, вам нужно создать экземпляр DataContractJsonSerializerSettings
и передать его DataContractJsonSerializer
со следующими установленными свойствами. Обратите внимание, что так как мы требуем UseSimpleDictionaryFormat
настройка, это решение будет работать только с. Net 4.5 или более поздней.
var settings = new DataContractJsonSerializerSettings();
settings.DataContractSurrogate = new MyDataContractSurrogate();
settings.KnownTypes = new List<Type> { typeof(Dictionary<string, Dictionary<string, object>>) };
settings.UseSimpleDictionaryFormat = true;
Вот демо:
public class Program
{
public static void Main(string[] args)
{
string json = @"
{
""company_webhooks"": [
{
""company_webhook"": {
""id"": 42,
""url"": ""https://keeptruckin.com/callbacktest/842b02"",
""secret"": ""fe8b75de0a4e5898f0011faeb8c93654"",
""format"": ""json"",
""actions"": [
""vehicle_location_received"",
""vehicle_location_updated""
],
""enabled"": false
}
},
{
""company_webhook"": {
""id"": 43,
""url"": ""https://keeptruckin.com/callbacktest/a6a783"",
""secret"": ""66a7368063cb21887f546c7af91be59c"",
""format"": ""json"",
""actions"": [
""vehicle_location_received"",
""vehicle_location_updated""
],
""enabled"": false
}
},
{
""company_webhook"": {
""id"": 44,
""url"": ""https://keeptruckin.com/callbacktest/53a52c"",
""secret"": ""4451dc96513b3a67107466dd2c4d9589"",
""format"": ""json"",
""actions"": [
""vehicle_location_received"",
""vehicle_location_updated""
],
""enabled"": false
}
},
{
""company_webhook"": {
""id"": 45,
""url"": ""https://keeptruckin.com/callbacktest/6fb337"",
""secret"": ""4177fbd88c30faaee03a4362648bd663"",
""format"": ""json"",
""actions"": [
""vehicle_location_received"",
""vehicle_location_updated""
],
""enabled"": false
}
},
{
""company_webhook"": {
""id"": 46,
""url"": ""https://keeptruckin.com/callbacktest/8cd6da"",
""secret"": ""6e41817a048b009435e5102fca17db55"",
""format"": ""json"",
""actions"": [
""vehicle_location_received"",
""vehicle_location_updated""
],
""enabled"": false
}
}
],
""pagination"": {
""per_page"": 25,
""page_no"": 1,
""total"": 5
}
}";
var settings = new DataContractJsonSerializerSettings();
settings.DataContractSurrogate = new MyDataContractSurrogate();
settings.KnownTypes = new List<Type> { typeof(Dictionary<string, Dictionary<string, object>>) };
settings.UseSimpleDictionaryFormat = true;
KeepTruckinResponse response = Deserialize<KeepTruckinResponse>(json, settings);
foreach (KeepTruckinCompanyWebHook wh in response.WebHooks)
{
Console.WriteLine("Id: " + wh.Id + ", Url: " + wh.Url);
}
}
public static T Deserialize<T>(string json, DataContractJsonSerializerSettings settings)
{
using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(json)))
{
var ser = new DataContractJsonSerializer(typeof(T), settings);
return (T)ser.ReadObject(ms);
}
}
public static string Serialize(object obj, DataContractJsonSerializerSettings settings)
{
using (MemoryStream ms = new MemoryStream())
{
var ser = new DataContractJsonSerializer(obj.GetType(), settings);
ser.WriteObject(ms, obj);
return Encoding.UTF8.GetString(ms.ToArray());
}
}
}
[DataContract]
public class KeepTruckinResponse
{
[DataMember(Name = "company_webhooks", EmitDefaultValue = false)]
public KeepTruckinCompanyWebHook[] WebHooks { get; set; }
[DataMember(Name = "pagination", EmitDefaultValue = false)]
public KeepTruckinPagination Pagination { get; set; }
public string RawJSON { get; set; }
}
[DataContract]
public class KeepTruckinPagination
{
[DataMember(Name = "per_page", EmitDefaultValue = false)]
public int PerPage { get; set; }
[DataMember(Name = "page_no", EmitDefaultValue = false)]
public int PageNumber { get; set; }
[DataMember(Name = "total", EmitDefaultValue = false)]
public int Total { get; set; }
}
[DataContract(Name = "company_webhook")]
public class KeepTruckinCompanyWebHook
{
[DataMember(Name = "id", EmitDefaultValue = false)]
public int Id { get; set; }
[DataMember(Name = "url", EmitDefaultValue = false)]
public string Url { get; set; }
}
Выход:
Id: 42, Url: https://keeptruckin.com/callbacktest/842b02
Id: 43, Url: https://keeptruckin.com/callbacktest/a6a783
Id: 44, Url: https://keeptruckin.com/callbacktest/53a52c
Id: 45, Url: https://keeptruckin.com/callbacktest/6fb337
Id: 46, Url: https://keeptruckin.com/callbacktest/8cd6da