1

I have some Json that is returned from the Formidable Forms API but I am struggling to convert it to a (c#) list of objects. The returned json isn't an array of objects so when I attempt to deserialize to an object (using newtonsoft.json) I get an error:

Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List...because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly.

Here is a snip of the returned Json.

{
    "mrjgb": {
        "id": "50",
        "item_key": "mrjgb",
        "name": "John",
        "ip": "",
        "meta": {
            "first_name": "John",
            "middle_initial": "T",
            "last_name": "Doe",            
            "date": "August 15, 2019",
            "date-value": "2019-08-15"
        },
        "form_id": "15",
        "post_id": "0",
        "user_id": "0",
        "parent_item_id": "0",
        "is_draft": "0",
        "updated_by": "0",
        "created_at": "2019-08-15 18:10:59",
        "updated_at": "2019-08-15 18:10:59"
    },
    "9rs0q": {
        "id": "48",
        "item_key": "9rs0q",
        "name": "dsdds",
        "ip": "",
        "meta": {
            "first_name": "dsdds",
            "middle_initial": "",
            "last_name": "23112qead",            
            "date": "August 13, 2019",
            "date-value": "2019-08-13"
        },
        "form_id": "15",
        "post_id": "0",
        "user_id": "25",
        "parent_item_id": "0",
        "is_draft": "0",
        "updated_by": "25",
        "created_at": "2019-08-13 13:43:23",
        "updated_at": "2019-08-13 13:43:23"
    }
}

In this snip there are two objects, mrjgb and 9rs0q, but there could be any number of these objects and they have no set name.

Can someone please point me in the right direction? I've made many attempts with varying degrees of success.

//List<Foo> foo = JsonConvert.DeserializeObject<List<Foo>>( response.Content );

//var Foo = JsonConvert.DeserializeObject<List<Foo>>( response.Content, new FooConverter() );
//var results = JsonConvert.DeserializeObject<List<dynamic>>( response.Content );

var FooList = new List<Foo>();
var results = JsonConvert.DeserializeObject<dynamic>( response.Content );

//var results33 = JsonConvert.DeserializeObject<Foo>( results );
//var Foos = JsonConvert.DeserializeObject<List<Foo>>( results, new FooConverter() );

foreach (var token in results )
{
    var Foo = JsonConvert.DeserializeObject<Foo>( token, new FooConverter() );
    //var Foo = JsonConvert.DeserializeObject<Foo>( results, new FooConverter() ); // passes all tokens, not just the one we want to convert
    FooList.Add( Foo );
}

//var Foo = JsonConvert.DeserializeObject<Foo>( response.Content );

EDIT:

With a little help from this post I was able to get a little bit closer. I can get a list of keys, but only if I know the source name (mrjgb in this case), but the name is random so I don't know it ahead of time....

        JToken outer = JToken.Parse( response.Content );
        JObject inner = outer[ "mrjgb" ].Value<JObject>();

        List<string> keys = inner.Properties().Select( p => p.Name ).ToList();
mack
  • 2,715
  • 8
  • 40
  • 68
  • Are mrjgb and 9rs0q the same type of object? if so, it means there can be more than one of them but with different (and not defined) names, right? – Rigoberto Ramirez Cruz Aug 15 '19 at 20:21
  • @RigobertoRamirezCruz, yes, they are the same type of object but the names are not defined and there can be any number of them. – mack Aug 15 '19 at 20:22
  • 2
    Use a `Dictionary` where `T` is a class to represent the item. See [How can I parse a JSON string that would cause illegal C# identifiers?](https://stackoverflow.com/q/24536533/10263). – Brian Rogers Aug 16 '19 at 05:24
  • Sorry @BrianRogers, I didn't see that post when I searched. – mack Aug 16 '19 at 14:38

2 Answers2

2

The API response is a JSON object, which translates to a .NET Dictionary<string, ?>. Instead of deserializing to a List or dynamic, use Dictionary<string, FormidableFormsResponse>:

var response = JsonConvert.DeserializeObject<Dictionary<string, FormidableFormsResponse>>(json);
Console.WriteLine($"{response.Keys.Count} items in the response");

var firstItem = response.ElementAt(0).Value;
Console.WriteLine(firstItem.Name);

var secondItem = response.ElementAt(1).Value;
Console.WriteLine(secondItem.Name);

I used QuickType to quickly generate the classes that represent the API response structure:

public class FormidableFormsResponse
{
    [JsonProperty("id")]
    public long Id { get; set; }

    [JsonProperty("item_key")]
    public string ItemKey { get; set; }

    [JsonProperty("name")]
    public string Name { get; set; }

    [JsonProperty("ip")]
    public string Ip { get; set; }

    [JsonProperty("meta")]
    public Meta Meta { get; set; }

    [JsonProperty("form_id")]
    public long FormId { get; set; }

    [JsonProperty("post_id")]
    public long PostId { get; set; }

    [JsonProperty("user_id")]
    public long UserId { get; set; }

    [JsonProperty("parent_item_id")]
    public long ParentItemId { get; set; }

    [JsonProperty("is_draft")]
    public long IsDraft { get; set; }

    [JsonProperty("updated_by")]
    public long UpdatedBy { get; set; }

    [JsonProperty("created_at")]
    public DateTimeOffset CreatedAt { get; set; }

    [JsonProperty("updated_at")]
    public DateTimeOffset UpdatedAt { get; set; }
}

public class Meta
{
    [JsonProperty("first_name")]
    public string FirstName { get; set; }

    [JsonProperty("middle_initial")]
    public string MiddleInitial { get; set; }

    [JsonProperty("last_name")]
    public string LastName { get; set; }

    [JsonProperty("date")]
    public string Date { get; set; }

    [JsonProperty("date-value")]
    public DateTimeOffset DateValue { get; set; }
}

Try it: fiddle

Nate Barbettini
  • 51,256
  • 26
  • 134
  • 147
  • Thanks Nate! Your example was very helpful and pointed me in the right direction. – mack Aug 16 '19 at 14:38
  • @mack You're welcome! I just realized I forgot to link to QuickType. (added) Super useful tool for quickly turning a JSON blob into classes. – Nate Barbettini Aug 16 '19 at 15:28
0

You can parse it via JsonConverter.DeserializeObject method which returns you an object (JObject). Then you can work with this object, where mrjgb will be JProperty and 9rs0q will be another JProperty from the same object (JObject)

var result = (JObject) JsonConvert.DeserializeObject(response.Content);

foreach (var r in result.Children())
{
     // Do what you need to do here,  r.First is the mrjgb/9rs0q/whatever object
     var _foo = r.First.ToObject<Foo>();
     FooList.Add( Foo );
}