Search code examples
c#jsonjson.netformidable

Formidable Forms API JSON Response to C# Object


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

Solution

  • 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