Десериализация 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.


person katit    schedule 03.10.2017    source источник
comment
Отредактируйте свой вопрос, чтобы показать, что вы сериализуете данные. Использование DataContract обычно избегается в пользу гораздо более используемых атрибутов NewtonSoft.Json.   -  person Camilo Terevinto    schedule 04.10.2017
comment
Очевидно, что при десериализации JSON я не получаю свойства KeepTruckinCompanyWebHook, потому что способ отправки коллекции является вложенным. Не уверен, что вы имеете в виду под этим. Как человек, использующий Newtonsoft, я очень ожидал, что это будет десериализовано.   -  person Carlos Rodriguez    schedule 04.10.2017


Ответы (1)


Да, это возможно, но для этого вам понадобится специальный код.

Это немного уродливо, но вы можете создать собственный _ 1_, чтобы десериализовать каждый объект 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
person Brian Rogers    schedule 07.10.2017