Search code examples
c#json.netexpandoobject

Deserialize an ExpandoObject into Object


I have an object that will be used with multiple clients. The object has a set of fixed properties that are common among all clients. Here is an example of what an object may look like:

public class Applicant
{
    public string ApplicantId { get; set; }
    public string ProducerId { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Address { get; set; }
    ...

    public List<dynamic> Information { get; set; }
}

We will be receiving this through a Web API POST. So I have this controller signature:

[HttpPost]
    public void Post([FromBody] dynamic value)
    {
        var converter = new ExpandoObjectConverter();
        dynamic input = JsonConvert.DeserializeObject<Loan>(value,converter);


    }

But when I run this code, NewtonSoft keeps giving me the error

Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: 'The best overloaded method match for 'Newtonsoft.Json.JsonConvert.DeserializeObject<ExpandoObjectTestAPI.Models.Loan>(string, params Newtonsoft.Json.JsonConverter[])' has some invalid arguments'

Looking around it appears that it is having problem with the dynamic type that is passed in. So how would I pass in this JSON so I can parse it using the ExpandoObjectConverter?


Solution

  • This doesn't answer the question using dynamic, but it's worth considering avoiding this by using a Dictionary instead to model custom properties. The following works for generic key/value pairs:

    public class Applicant
    {
       ...Standard properties
    
       public IDictionary<string, object> Custom { get; set; }
    }
    
    

    It would accept JSON with arbitrary dynamic fields nested under your "Custom" namespace:

    {
        "applicationId": "123",
        "producerId": "456",
        "custom": {
            "favoriteAnimal": "turtles",
            "someRandomMetadata": 42,
            "someUnforeseenList": ["foo", "bar"]
        }
     }
    

    I've had good luck handling user-defined extension fields this way. It also has the arguable advantage of isolating custom fields into a nested namespace to isolate from built-in data.