属性具有IDictionary <string,string =“”>

时间:2018-12-26 20:11:10

标签: c# json.net deserialization

我看到了这个,但是对我没有帮助。

Deserializing an object with a Dictionary<string, object> property (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) 例如可以从JSON数组反序列化的List。还可以将JsonArrayAttribute添加到类型中,以强制其从JSON数组反序列化。 路径“ TimestampExtraInfoPackages [0] .ExtraInfo”,第1行,位置194。

解决方案:

感谢湿婆,布莱恩和塔弗。我使用了taffer建议,并且有效。看来我们不能使用IDictionary。

我在下面用来解决我的问题。

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

我在下面尝试过,但没有工作,出现了相同的错误。

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

3 个答案:

答案 0 :(得分:4)

如错误消息所述,传入的ExtraInfo应该是一个JSON对象,但在您的示例中,它是一个包含具有KeyValue属性的对象的JSON数组。

使用预期的JSON字符串来提供服务:

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

或将合同修改为数组或列表,以便它可以接受JSON数组:

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

答案 1 :(得分:1)

您的ExtraInfo声明应为数组。

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

代替

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

编辑

这是工作代码。我尝试将其放在dotNetFiddle上,但在dotnetfiddle.com上出现一些错误[包括System.Runtime.Serialization,因此将下面的工作代码粘贴到了

请注意,您的ExtraInfo如下所示。但这也是JSON的方式。如果要分别从KeyValue json键中获取值并构造一个字典或键值对条目,则可能必须编写一个https://github.com/Pyomo/PyomoGallery/wiki/Min-cost-flow

custom Converter

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());

        }
    }
}

答案 2 :(得分:0)

您收到此错误,因为您的JSON具有一个键-值对对象数组,而一个字典被序列化为一个对象。

一种解决方案是按照@taffer的建议将ExtraInfo属性更改为KeyValuePair<string, string>[]以匹配JSON(或更改JSON以匹配您的类)。

但是,还有另一种方法。您可以将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将其视为键值对对象的数组。有关此方法的更多信息,请参见Serialize dictionary as array (of key value pairs)。但是,如果采用这种方式,则需要将ExtraInfo属性的声明从IDictionary接口更改为具体的Dictionary,否则Json.Net会给您一个错误的提示能够创建接口的实例。