48

When I can call the 3rd party api and get back a single class worth of data everything deserialises fine using this code

TheUser me = jsonSerializer.Deserialize(response, typeof(TheUser)) as TheUser

The problem comes when I try and deserialise JSON response content that is an array, such as

{
   "data": [
      {
         "name": "A Jones",
         "id": "500015763"
      },
      {
         "name": "B Smith",
         "id": "504986213"
      },
      {
         "name": "C Brown",
         "id": "509034361"
      }
   ]
}

I can only get the serialization to work if I use a custom wrapping class around the "data" member and that member needs to be of type List<object>. If it have them as type List<TheUser> I get ArgumentException from the JsonParser DesializeType method.

I originally tried to serialise without a wrapping type like this

List<TheUser> freinds = jsonSerializer.Deserialize(response, typeof(List<TheUser>)) as List<TheUser>;

but that just returns me an empty collection. Surely I must be able to have the array deserialize to a strongly typed list.

jgauffin
  • 99,844
  • 45
  • 235
  • 372
Pat Long - Munkii Yebee
  • 3,592
  • 2
  • 34
  • 68
  • I have the same problem; if you deserialize `response` into a `dynamic` var instead of `TheUser`, then `deserialized.data` is available as a `JArray` -- any idea if you can convert that into `List`? – drzaus Jan 25 '13 at 15:43
  • Seems you should be able to ([link](http://www.west-wind.com/weblog/posts/2012/Aug/30/Using-JSONNET-for-dynamic-JSON-parsing)). – drzaus Jan 25 '13 at 16:15

8 Answers8

34

Afer looking at the source, for WP7 Hammock doesn't actually use Json.Net for JSON parsing. Instead it uses it's own parser which doesn't cope with custom types very well.

If using Json.Net directly it is possible to deserialize to a strongly typed collection inside a wrapper object.

var response = @"
    {
        ""data"": [
            {
                ""name"": ""A Jones"",
                ""id"": ""500015763""
            },
            {
                ""name"": ""B Smith"",
                ""id"": ""504986213""
            },
            {
                ""name"": ""C Brown"",
                ""id"": ""509034361""
            }
        ]
    }
";

var des = (MyClass)Newtonsoft.Json.JsonConvert.DeserializeObject(response, typeof(MyClass));

return des.data.Count.ToString();

and with:

public class MyClass
{
    public List<User> data { get; set; }
}

public class User
{
    public string name { get; set; }
    public string id { get; set; }
}

Having to create the extra object with the data property is annoying but that's a consequence of the way the JSON formatted object is constructed.

Documentation: Serializing and Deserializing JSON

James Newton-King
  • 48,174
  • 24
  • 109
  • 130
Matt Lacey
  • 65,560
  • 11
  • 91
  • 143
  • It is very easy to change DeSerializer in Hammock. Create a class that implements the Hammock.Serialization.IDeserializer interface. And then set that class as your Deserializer on the RestClient. – Dennis Skantz Sep 29 '11 at 14:41
  • I agree that it's slightly annoying to add the extra object, hence my solution below. My approach also gets around you having to case like you are in the var des = (MyClass)... line. Not sure if either this or my solution is better than the other, just prefer not having to add the extra Data object and I generally try to avoid type casting. – Erick Brown Mar 23 '15 at 16:59
  • How much does it make to first do a `Json.Parse(jsonString)` and then do a `JsonConvert.DeserializeObject(parsedJson["data"]).ToString())` to finally obtain a list of `User`, performance and sanity wise? ;-) – Sнаđошƒаӽ Jun 18 '17 at 14:11
  • I read this thread multiple times debugging the same problem. In my case I had to get the JSON in the right format for processing by the JSON.Net package. What I would offer to others facing a similar problem is use a TraceWriter as the error message help highlight a fault I did not see in the JSON file. – rray Mar 01 '22 at 14:00
20

try

List<TheUser> friends = jsonSerializer.Deserialize<List<TheUser>>(response);
Frank
  • 3,029
  • 5
  • 34
  • 43
  • That doesn't compile, however isn't that just a differnt calling style that is going to result in the same underlying mthods being called in the same way? I do not see how that will get around my issue – Pat Long - Munkii Yebee May 13 '11 at 08:21
  • 4
    This worked for me: `List friends = JsonConvert.DeserializeObject>(response);` – Homer Jan 18 '13 at 19:28
  • 1
    Thanks! This allowed me to deserialize an array of objects without wrapping them in a container object. – masterwok Mar 05 '15 at 04:37
  • I like Homer's solution, I agree it's a bit cleaner than my solution as it obviates the need for having a parent class or having to implement an interface if you don't want to inherit from anything. -Eric – Erick Brown Apr 17 '15 at 00:21
  • this one works for me with similar problem. – Software Architect Nov 25 '22 at 06:03
12

This worked for me for deserializing JSON into an array of objects:

List<TheUser> friends = JsonConvert.DeserializeObject<List<TheUser>>(response);
Homer
  • 7,594
  • 14
  • 69
  • 109
9

This solution seems to work for me and gets around having to code a bunch of classes with "Data" in them.

public interface IDataResponse<T> where T : class
{
    List<T> Data { get; set; }
}

public class DataResponse<T> : IDataResponse<T> where T : class
{
   [JsonProperty("data")]
   public List<T> Data { get; set; }
}

I should have included this to begin with, here is an example method using the above:

public List<TheUser> GetUser()
{
    var results = GetUserJson();
    var userList = JsonConvert.DeserializeObject<DataResponse<TheUser>>(results.ToString());

    return userList.Data.ToList();
} 
Erick Brown
  • 618
  • 11
  • 22
8

Json.NET - Documentation

http://james.newtonking.com/json/help/index.html?topic=html/SelectToken.htm

Interpretation for the author

var o = JObject.Parse(response);
var a = o.SelectToken("data").Select(jt => jt.ToObject<TheUser>()).ToList();
bobah75
  • 3,500
  • 1
  • 16
  • 25
2

Pat, the json structure looks very familiar to a problem i described here - The answer for me was to treat the json representation as a Dictionary<TKey, TValue>, even though there was only 1 entry.

If I am correct your key is of type string and the value of a List<T> where T represents the class 'TheUser'

HTH

PS - if you want better serialisation perf check out using Silverlight Serializer, you'll need to build a WP7 version, Shameless plug - I wrote a blog post about this

Community
  • 1
  • 1
AwkwardCoder
  • 24,893
  • 27
  • 82
  • 152
1

I like this approach, it is visual for me.

using (var webClient = new WebClient())
    {
          var response = webClient.DownloadString(url);
          JObject result = JObject.Parse(response);
          var users = result.SelectToken("data");   
          List<User> userList = JsonConvert.DeserializeObject<List<User>>(users.ToString());
    }
0

I suspect the problem is because the json represents an object with the list of users as a property. Try deserializing to something like:

public class UsersResponse
{
    public List<User> Data { get; set; }
}
Nigel Sampson
  • 10,549
  • 1
  • 28
  • 31