I saw this but it doesnot help me.
Deserializing an object with a Dictionary<string, object> property (Newtonsoft.Json)
I am working on WCF Restful service. I have complicated JSON. I have below data contracts
[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; }
}
I am making REST service call like below
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);
I am getting JSON string like below
{
"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": []
}
Below is the error I am getting
Cannot deserialize the current JSON array (eg [1,2,3]) into type 'System.Collections.Generic.IDictionary`2[System.String,System.String]' because the type requires a JSON object (eg {"name":"value"}) to deserialize correctly. To fix this error either change the JSON to a JSON object (eg {"name":"value"}) or change the deserialized type to an array or a type that implements a collection interface (eg ICollection, IList) like List that can be deserialized from a JSON array. JsonArrayAttribute can also be added to the type to force it to deserialize from a JSON array. Path 'TimestampExtraInfoPackages[0].ExtraInfo', line 1, position 194.
SOLUTION:
Thanks to Shiva, Brian and taffer. I used taffer advice and it worked. It looks we cannot use IDictionary.
I used below to solve my issue.
public List<KeyValuePair<string, string>> ExtraInfo { get; set; }
I tried below but did not work got same error.
public IDictionary<string, string>[] ExtraInfo { get; set; }
As the error message says the incoming ExtraInfo
should be a JSON object but in your example it is a JSON array containing objects with Key
and Value
properties.
Either feed your service with the expected JSON string:
"ExtraInfo": {
"Key1": "value1",
"Key2": "value2"
}
Or modify your contract to an array or list so it can accept a JSON array:
[DataMember]
public KeyValuePair<string, string>[] ExtraInfo { get; set; }
Your declaration of ExtraInfo
should be an array.
public IDictionary<string, string>[] ExtraInfo { get; set; }
instead of
public IDictionary<string, string> ExtraInfo { get; set; }
EDIT
Here's the working code. I tried putting it on dotNetFiddle, but was/am getting some errors on dotnetfiddle.com [ including System.Runtime.Serialization
], hence pasting the working code below.
Note, your ExtraInfo
will look like the following. But that's how your JSON also is. If you want to take the values from the Key
and Value
json keys respectively and construct a single dictionary or keyvaluepair entry, then you'd likely have to write a 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());
}
}
}
You are getting this error because your JSON has an array of key-value pair objects, whereas a dictionary is serialized as a single object.
One solution is to change your ExtraInfo
property to be a KeyValuePair<string, string>[]
to match the JSON (or change the JSON to match your class) as suggested by @taffer.
However, there is another way. You could keep your ExtraInfo
property declared as it is and use a custom JsonConverter
to create and populate the dictionary during deserialization.
Here is the code you would need for the converter:
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();
}
}
To use the converter, just add a [JsonConverter]
attribute to your ExtraInfo
property like this:
[DataMember]
[JsonConverter(typeof(KvpArrayToDictionaryConverter))]
public IDictionary<string, string> ExtraInfo { get; set; }
Working demo: https://dotnetfiddle.net/1k7vNA
Another possible solution is to use a ContractResolver
to change the contract of the dictionary so that Json.Net sees it as an array of key-value pair objects. See Serialize dictionary as array (of key value pairs) for more info on this approach. However if you go that route you will need to change the declaration of your ExtraInfo
property from an IDictionary
interface to a concrete Dictionary
, otherwise Json.Net will give you an error about not being able to create an instance of an interface.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.