1

Trying to figure out my serialization problem.

When I make a post request to my LoadById method I can't get to serialize my User class, obtaining a MyNamespace.User response instead of a JSON serialized User payload. Other methods work without any issues.

This is Web.Api ApiController..

public class UserServiceController : System.Web.Http.ApiController
{ 
  [HttpPost]
  public User LoadById(String id)
  {
     using (UserManager mng = new UserManager())
     {         
        user = mng.LoadById(String id);

        //string json = JsonConvert.SerializeObject(user, new JsonSerializerSettings { TraceWriter = traceWriter });
        //Console.WriteLine(traceWriter);
     }
     return user;
  }
}

This is the trace for the serialization:

2017-11-13T15:55:58.237 Info Started serializing MyNamespace.User. Path ''.
2017-11-13T15:55:58.237 Info Finished serializing MyNamespace.User. Path ''.
2017-11-13T15:55:58.237 Verbose Serialized JSON: "MyNamespace.User"

Json as string:

 "MyNamespace.User"

User class:

The user class is quite complex, i only have access to the metadata that 
doesn't show much.

how can I get more info to understand what's happening?

StuartLC
  • 104,537
  • 17
  • 209
  • 285
Tom
  • 302
  • 2
  • 5
  • 14
  • System.Web.Http.ApiController. :( (thanks for your try) – Tom Nov 13 '17 at 16:17
  • 2
    To confirm, is the the issue that a POST to your controller route returns the string `MyNamespace.User`, instead of a JSON serialized `User` payload? – StuartLC Nov 13 '17 at 16:20
  • 2
    Can you share your `User` class with us? – Brian Rogers Nov 13 '17 at 16:33
  • Hard to say what is happening without a [mcve]. Maybe your `User` class has a `TypeConverter` specified? If so Json.NET will use it to convert instances of `User` to strings, see [Newtonsoft.JSON cannot convert model with TypeConverter attribute](https://stackoverflow.com/q/31325866/3744182) for details and a workaround. – dbc Nov 13 '17 at 17:00
  • Thanks for you answer dbc and Brian Rogers. I can't access the User class, so I can't use DataContracts. Thinking of "creating a clean POCO ViewModel custom built for your Api" as user StuartLC suggested. – Tom Nov 13 '17 at 17:10
  • 1
    @Tom - to test if a `TypeConverter` is causing the problem, you could try adding `NoTypeConverterJsonConverter` from [this answer](https://stackoverflow.com/a/31328131/3744182) to `JsonSerializerSettings.Converters`. Json.NET will also serialize a object as a string if it implements `IConvertible`. You can check that easily in the debugger by testing `user is IConvertible`. Either way a `ViewModel` may be the best solution, but at least you will know why it is happening. – dbc Nov 13 '17 at 19:48

2 Answers2

2

Seemingly, NewtonSoft JsonConvert is unable to serialize your Users class for some reason, and instead of providing Json, is simply returning the name of the class, which would be consistent with the default .ToString() behaviour (i.e. no override) of a reference type.

As an alternative, it is generally a good idea to provide clean, dedicated ViewModel classes, or even anonymous class projections, when returning data from your WebApi.

This way, the separation of concerns between Domain objects (such as the User object used with the AspNet Identity UserManager) is encapsulated from the payload that clients of your WebApi would be coupled to - this way you can control exactly which fields are exposed on the Api.

[HttpPost]
public IHttpActionResult LoadById(String id)
{
   using (var mng = new UserManager())
   {         
      var domainUser = mng.LoadById(String id);

      var apiUser = new MyUserViewModel
      {
          // Map visible properties ...
          Name = domainUser.UserName,
          // Change the shape
          Address = domainUser.Address.Postal,
          ...
          // Hide fields that shouldn't be externally accessed
      };
      return Ok(apiUser);
   }
}

Other Causes

This commonly happens if inherit your Controller from System.Web.MvcController instead of WebApi's System.Web.Http.ApiController. By Example:

public class Foo
{
    public string Name { get; set; }
}

public class MyController : System.Web.MvcController.Controller
{
    [HttpGet]
    public Foo Index()
    {
        return new Foo{Name = "Bob"};
    }
}

GET on /MyController returns the default ToString() of the class (which will default to the name of the type)

`MyNameSpace.Foo`

What you need to do of course is:

public class MyController : **ApiController**
{
    [HttpGet]
    public Foo Index()
    {
        return new Foo{Name = "Bob"};
    }
}

Will give the required

{
   Name: "Bob"
}
StuartLC
  • 104,537
  • 17
  • 209
  • 285
  • Thanks for your help but it's a System.Web.Http.ApiController baseClass. – Tom Nov 13 '17 at 16:39
  • It seems that Newtonsoft is unable to serialize your `User` class for some reason. Here's the dump applying the same `TraceWriter` to the above - as you can see, `JsonConvert` is able to serialize my Poco. "2017-11-13T18:38:45.564 Info Started serializing WebApplication1.Controllers.Foo. Path ''." "2017-11-13T18:38:45.565 Info Finished serializing WebApplication1.Controllers.Foo. Path ''." "2017-11-13T18:38:45.565 Verbose Serialized JSON: \r\n{\r\n \"Name\": \"Bob\"\r\n}" – StuartLC Nov 13 '17 at 16:41
  • 1
    I would suggest that you consider creating a clean `POCO ViewModel` custom built for your Api, with tighter control over what fields are being exposed, and then map your domain `User` object across to this ViewModel User. – StuartLC Nov 13 '17 at 16:48
  • Good idea, instead of playing around with DataContracts. Can you please change your answer so I can vote it. – Tom Nov 13 '17 at 17:00
  • I've updated, but I would still be interested to know what it is that stumped NewtonSoft like that :) – StuartLC Nov 13 '17 at 17:47
0

Sorry for the delay..

I finally had the chance to check all the inherited types of my class User, and during my analysis my eyes stopped on a TypeConverter attribute.

So I remembered what user dbc suggested in a comment, try with NoTypeConverterJsonConverter, as suggested in the following answer.

Newtonsoft.JSON cannot convert model with TypeConverter attribute

Thanks to everybody for your help!

Tom
  • 302
  • 2
  • 5
  • 14