Десериализовать объект, когда свойство имеет IDictionary - PullRequest
0 голосов
/ 26 декабря 2018

Я видел это, но это не помогает мне.

Десериализация объекта с помощью словарясвойство (Newtonsoft.Json)

Я работаю на WCF Restful службы.У меня сложный JSON.У меня ниже контракты данных

[DataContract]
public class TimestampPackageDC
{
    [DataMember]
    public string Timestamp { get; set; }

    [DataMember]
    public string Company { get; set; }

    [DataMember]
    public string ContactID { get; set; }

    [DataMember]
    public string Area { get; set; }

    [DataMember]
    public string PackageID { get; set; }
} // end of class TimestampPackageDC

[DataContract]
public class TimestampPackageExtraDC: TimestampPackageDC
{
    [DataMember]
    public IDictionary<string, string> ExtraInfo { get; set; }
} // end of class TimestampPackageExtraDC


 [DataContract]
public class GetAllUnprintedItemsResponse: BaseResponse
{
    [DataMember]
    public TimestampPackageDC[] TimestampPackages { get; set; }

    [DataMember]
    public TimestampPackageExtraDC[] TimestampExtraInfoPackages { get; set; }

} 

Я делаю вызов службы REST, как показано ниже

GetAllUnprintedItemsResponse upResponse = new GetAllUnprintedItemsResponse();
Request upRequest = new Request() { Name = "abc" };
string url = "http://localhost:2023/myservice/getdata";
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "POST";
request.ContentType = "application/json";
byte[] data = Encoding.UTF8.GetBytes(Newtonsoft.Json.JsonConvert.SerializeObject(upRequest));
request.ContentLength = data.Length;
Stream requestStream = request.GetRequestStream();
requestStream.Write(data, 0, data.Length);
requestStream.Close();

WebResponse response = request.GetResponse();
Stream respStream = response.GetResponseStream();
StreamReader reader = new StreamReader(respStream);
string responseData = reader.ReadToEnd();

//getting error below
Newtonsoft.Json.JsonConvert.PopulateObject(responseData, upResponse);

Я получаю строку JSON, как показано ниже

{
    "DocumentException": null,
    "Success": true,
    "TimestampExtraInfoPackages": [{
        "Area ": "AA",
        "Company": "XXX",
        "ContactID": "123",
        "PackageID": "P1",
        "Timestamp": "20090501163433360001",
        "ExtraInfo": [{
                "Key": "Key1",
                "Value": "value1"
            },
            {
                "Key": "Key2",
                "Value": "value2"
            }
        ]
    }],
    "TimestampPackages": []
}

Ниже приведенЯ получаю сообщение об ошибке

Невозможно десериализовать текущий массив JSON (например, [1,2,3]) в тип 'System.Collections.Generic.IDictionary`2 [System.String, System.String]'потому что тип требует JSON-объект (например, {"name": "value"}) для правильной десериализации.Чтобы исправить эту ошибку, либо измените JSON на объект JSON (например, {"name": "value"}), либо измените десериализованный тип на массив или тип, который реализует интерфейс коллекции (например, ICollection, IList), например List, который можетбыть десериализованным из массива JSON.JsonArrayAttribute также может быть добавлен к типу, чтобы принудительно десериализовать его из массива JSON.Путь TimestampExtraInfoPackages [0] .ExtraInfo, строка 1, позиция 194.

РЕШЕНИЕ:

Благодаря Шиве, Брайану и Тафферу.Я использовал совет Таффера, и это сработало.Похоже, мы не можем использовать IDictionary.

Я использовал ниже, чтобы решить мою проблему.

public List<KeyValuePair<string, string>> ExtraInfo { get; set; }

Я попытался ниже, но не работает, получил ту же ошибку.

public IDictionary<string, string>[] ExtraInfo { get; set; }

Ответы [ 3 ]

0 голосов
/ 26 декабря 2018

Ваше объявление ExtraInfo должно быть массивом.

public IDictionary<string, string>[] ExtraInfo { get; set; }

вместо

public IDictionary<string, string> ExtraInfo { get; set; }

EDIT

Вот рабочий код.Я пытался поместить его в dotNetFiddle, но на dotnetfiddle.com [/ * были получены некоторые ошибки] [включая System.Runtime.Serialization], поэтому вставил рабочий код ниже.

Обратите внимание, ваш ExtraInfo будет выглядеть следующим образом.Но таков твой JSON.Если вы хотите взять значения из ключей Key и Value json соответственно и создать одну запись словаря или пары значений ключей, то вам, вероятно, придется написать пользовательский конвертер .

enter image description here

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using Newtonsoft.Json;

namespace SoDeserializeJson_53936668
{
    [System.Runtime.Serialization.DataContract]
    public class TimestampPackageDC
    {
        [DataMember]
        public string Timestamp { get; set; }

        [DataMember]
        public string Company { get; set; }

        [DataMember]
        public string ContactID { get; set; }

        [DataMember]
        public string Area { get; set; }

        [DataMember]
        public string PackageID { get; set; }
    } // end of class TimestampPackageDC

    [DataContract]
    public class TimestampPackageExtraDC : TimestampPackageDC
    {
        [DataMember]
        public IDictionary<string, string>[] ExtraInfo { get; set; }
    } // end of class TimestampPackageExtraDC


    [DataContract]
    public class GetAllUnprintedItemsResponse //: BaseResponse
    {
        [DataMember]
        public TimestampPackageDC[] TimestampPackages { get; set; }

        [DataMember]
        public TimestampPackageExtraDC[] TimestampExtraInfoPackages { get; set; }

    }

    class Program
    {
        static void Main(string[] args)
        {

            var json = @"
            {
                'DocumentException': null,
                'Success': true,
                'TimestampExtraInfoPackages': [{
                    'Area ': 'AA',
                    'Company': 'XXX',
                    'ContactID': '123',
                    'PackageID': 'P1',
                    'Timestamp': '20090501163433360001',
                    'ExtraInfo': [{
                            'Key': 'Key1',
                            'Value': 'value1'
                        },
                        {
                            'Key': 'Key2',
                            'Value': 'value2'
                        }
                    ]
                }],
                'TimestampPackages': []
            }".Replace("'","\"");

            GetAllUnprintedItemsResponse upResponse = new GetAllUnprintedItemsResponse();

            string responseData = json;

            var data = JsonConvert.DeserializeObject<GetAllUnprintedItemsResponse>(json);

            Console.WriteLine(data.TimestampExtraInfoPackages.First().ExtraInfo.Count());

        }
    }
}
0 голосов
/ 27 декабря 2018

Вы получаете эту ошибку, потому что ваш JSON имеет массив объектов пары ключ-значение, тогда как словарь сериализуется как один объект.

Одним из решений является изменение свойства ExtraInfo на KeyValuePair<string, string>[] в соответствии с JSON (или изменение JSON в соответствии с вашим классом) в соответствии с предложением @ taffer.

Однакоесть другой способ.Вы можете оставить ваше свойство ExtraInfo объявленным как есть и использовать пользовательский JsonConverter для создания и заполнения словаря во время десериализации.

Вот код, который вам понадобится для конвертера:

public class KvpArrayToDictionaryConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return typeof(IDictionary<string, string>).IsAssignableFrom(objectType);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var dict = new Dictionary<string, string>();
        var array = JArray.Load(reader);
        foreach (var obj in array.Children<JObject>())
        {
            dict.Add((string)obj["Key"], (string)obj["Value"]);
        }
        return dict;
    }

    public override bool CanWrite
    {
        get { return false; }
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

Чтобы использовать конвертер, просто добавьте атрибут [JsonConverter] к свойству ExtraInfo, например:

    [DataMember]
    [JsonConverter(typeof(KvpArrayToDictionaryConverter))]
    public IDictionary<string, string> ExtraInfo { get; set; }

Рабочая демонстрация: https://dotnetfiddle.net/1k7vNA


Другим возможным решением является использование ContractResolver для изменения контракта в словаре, чтобы Json.Net мог видетьэто как массив объектов пары ключ-значение.См. Сериализация словаря в виде массива (пар значений ключей) для получения дополнительной информации об этом подходе.Однако если вы пойдете по этому пути, вам нужно будет изменить объявление вашего свойства ExtraInfo с интерфейса IDictionary на конкретный Dictionary, в противном случае Json.Net выдаст вам ошибку о невозможности создать экземпляринтерфейс.

0 голосов
/ 26 декабря 2018

Как говорится в сообщении об ошибке, входящий ExtraInfo должен быть объектом JSON, но в вашем примере это массив JSON, содержащий объекты со свойствами Key и Value.

Либо подпишите свой сервисожидаемая строка JSON:

"ExtraInfo": {
            "Key1": "value1",
            "Key2": "value2"
        }

Или измените ваш контракт на массив или список, чтобы он мог принимать массив JSON:

[DataMember]
public KeyValuePair<string, string>[] ExtraInfo { get; set; }
...