Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JSON.NET Abstract / Derived Class Deserialization with WebAPI 2

Tags:

I'm implementing a Web API 2 service that uses JSON.NET for serialization.

When I try to PUT ( deseralize ) updated json data, the abstract class is not present meaning it didn't know what to do with it so it did nothing. I also tried making the class NOT abstract and just inheriting from it and then each PUT deseralized to the base class rather than the derrived class missing the properties of the derrived class.

Example:

public class People
{
      // other attributes removed for demonstration simplicity

      public List<Person> People { get;set; }
}

public abstract class Person
{
      public string Id {get;set;}
      public string Name {get;set;}
}

public class Employee : Person 
{
      public string Badge {get;set;}
}

public class Customer : Person
{
     public string VendorCategory {get;set;}
}

with my web api configured to do typename handling:

public static void Register(HttpConfiguration config)
{
     config.Formatters.JsonFormatter.SerializerSettings.TypeNameHandling = 
            TypeNameHandling.Objects;
}

then I PUT the JSON like:

{
     people: [{
          name: "Larry",
          id: "123",
          badge: "12345",
          $type: "API.Models.Employee, API"
     }]
}

to the web api method:

public HttpResponseMessage Put(string id, [FromBody]People value)
{
      people.Update(value); // MongoDB Repository method ( not important here )
      return Request.CreateResponse(HttpStatusCode.OK);
}

but the output when inspecting value is always:

People == { People: [] }

or if non-abstract:

People == { People: [{ Name: "Larry", Id: "123" }] }

missing the inherrited property. Anyone ran into this problem and come up with anything?

like image 364
amcdnl Avatar asked Dec 05 '13 19:12

amcdnl


3 Answers

The $type function has to be the first attribute in the object.

In the above example I did:

 {    people: [{       name: "Larry",       id: "123",       badge: "12345",       $type: "API.Models.Employee, API"    }]  } 

after moving $type to the top like:

 {    people: [{       $type: "API.Models.Employee, API",       name: "Larry",       id: "123",       badge: "12345"    }]  } 

the serializer was able to deseralize the object to the correct cast. Gotta love that!

like image 85
amcdnl Avatar answered Nov 17 '22 09:11

amcdnl


I have tried your scenario now and it works fine. But I did notice that you are missing a , (comma) after the id property in your json input.

I figured this out by using the following ModelState validity check in my action which then showed the error in my request payload. This could be useful to you too:

if (!ModelState.IsValid)
{
    return Request.CreateErrorResponse(HttpStatusCode.BadRequest, this.ModelState);
}
like image 20
Kiran Avatar answered Nov 17 '22 10:11

Kiran


I know this post is old now and the answer has been marked, but I thought my solution might be helpful....

Try adding the JsonProperty attribute to the properties on your abstract class.

    using JTC.Framework.Json;
    ...
    public class People
    {
        // other attributes removed for demonstration simplicity

        public List<Person> People { get;set; }
    }

    public abstract class Person
    {
          [JsonProperty()]
          public string Id {get;set;}

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

    public class Employee : Person 
    {
          public string Badge {get;set;}
    }

    public class Customer : Person
    {
         public string VendorCategory {get;set;}
    }
like image 29
Dave Russell Avatar answered Nov 17 '22 10:11

Dave Russell