3

I have a custom type that I want the MVC model binder serialize/deserialize using my custom JsonConverter for System.Text.Json. Here is my implementation:

public class Enumeration 
{
  private readonly string _value;

  public Enumeration(string value)
  {
    _value = value;
  }
  //utility methods
}

public class EmployeeTypeEnum : Enumeration
{
   EmployeeTypeEnum(string value) : base(value)
   {}

   public static implicit operator string(EmployeeTypeEnum employee)
   {
      return employee.Value
   }

   public static implicit operator EmployeeTypeEnum(string value)
   {
      return new EmployeeTypeEnum(value);
   }

   //Other util methods
}

Custom converter:

public class EmployeeTypeEnumJsonConvertor : System.Text.Json.Serialization.JsonConvertor<EmployeeTypeEnum>
{
     public override EmployeeTypeEnum Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
     {
         //implementation
     }

     public override void Write(ref Utf8JsonWriter writer, EmployeeTypeEnum empEnum, JsonSerializerOptions options)
     {
         //implementation
     }
}

Type using the above:

public static class EmployeeType 
{
   public static readonly EmployeeTypeEnum Manager = "Manager";
}

Model using the employeeTypeEnum:

public class MyViewModel
{
   ...
   [JsonConverter(typeof(EmployeeTypeEnumJsonConvertor))]
   public EmployeTypeEnum EmployeeRank {get; set;}
   ...
}

Ajax call:

$.ajax({
   url: `https://localhost:8080/.../GetEmployeeRankInfo?Id=${id}&EmployeeRank=${rank}`,
   type: 'application/json',
   method: 'GET'
   ...
});

Controller:

public Task<IActionResult> GetEmployeeRankInfo(MyViewModel model)
{
   //Get employee Rank info
}

In the controller, the EmployeeRank is always null. I put a break point inside both Read and Write of the custom converter but none is hit. I also overrode the CanConvert method and not breaking in it there either. I went through a number of SO posts on this topic but most found a workaround. What could cause the custom serializer not to be called? Thanks

Stack Undefined
  • 1,050
  • 1
  • 14
  • 23
  • What version of ASP do you use? – Guru Stron Nov 08 '21 at 16:57
  • I'm using .NET core 5 – Stack Undefined Nov 08 '21 at 17:02
  • Was not able to reproduce. Can you post a full repro? Or check that `Startup` does not have `AddNewtonsoftJson`? Or that `JsonConverterAttribute` comes from `System.Text.Json.Serialization`, not `Newtonsoft.Json`? – Guru Stron Nov 08 '21 at 17:17
  • I don't see AddNewtonsoftJson() being called anywhere so I'm assuming it's using the default System.Text.Json serializer. Is there a way to tell programmatically whether a controller uses newtonsoft or system.text.json? – Stack Undefined Nov 08 '21 at 19:18
  • One other thing I noticed is that JsonConverter has just the CanConvert() method where as the JsonConverter which inherits from JsonConverter has the write and read methods. NewtonSoft's JsonConverter has all the methods. So I need to inherit the JsonConverter but still no dice. – Stack Undefined Nov 08 '21 at 19:21
  • You can add `[JsonProperty("test")]` attribute on one of properties of some model returned by some API. If it compiles and works then Newtonsoft is used. – Guru Stron Nov 08 '21 at 19:22
  • AFAIK you can't inherit non-generic [`JsonConverter`](https://learn.microsoft.com/en-us/dotnet/api/system.text.json.serialization.jsonconverter-1.-ctor?view=net-5.0) from `System.Text.Json` cause it has internal ctor available only to `JsonConverter`. It seems that you have Newtonsoft.Json added to project so try changing `[JsonConverter(typeof(EmployeeTypeEnumJsonConvertor))]` to `[System.Text.Json.Serialization.JsonConverter(typeof(EmployeeTypeEnumJsonConvertor))]`. – Guru Stron Nov 08 '21 at 19:24
  • 2
    You're not passing the `EmployeeRank` in the JSON, you're passing it in the ***URL parameters***, aren't you? A `JsonConverter` is only invoked for **JSON values** not URL parameter values. – dbc Nov 08 '21 at 19:36
  • No dice with System.Text.Json. I'm switching everything over to Newtonsoft to see if I can get this custom converter working. So far even the newtonsoft version of the converter doesn't hit the breakpoint. It's like whatever the serializer the model binder is using doesn't even know there is a custom converter. Just to see what happens, I manually assigned a string to the property `public EmployeeTypeEnum EmployeeRank {get; set;} = "CEO";` It was deserialized in the controller as `EmployeeRank = {CEO}` but no converter was invoked and converter wasn't even need. – Stack Undefined Nov 08 '21 at 20:40
  • 1
    @StackUndefined *It's like whatever the serializer the model binder is using doesn't even know there is a custom converter* - exactly. No matter whether you use Json.NET or System.Text.Json, custom JSON converters are only invoked to bind JSON, not to bind URL Parameters like `https://localhost:8080/.../GetEmployeeRankInfo?Id=${id}&EmployeeRank=${rank}`. See e.g. [Custom model binder for QueryString string parameters in ASP.NET Core 3.1?](https://stackoverflow.com/q/62277271/3744182). – dbc Nov 08 '21 at 21:39
  • @dbc - Thanks for the help. I implemented a custom model binder and it works. However, it feels so hacky. I'm actually rummaging through types and manually instantiating the correct object in the model binder. Is ajax post sending models in request body considered JSON? In my case, the model binder grabs the post call and the custom json converter never gets called. – Stack Undefined Nov 09 '21 at 04:08
  • 2
    @StackUndefined - According to the [original JSON proposal](https://www.json.org/json-en.html), *JSON (JavaScript Object Notation) is a lightweight data-interchange format.* Think XML, only simpler. When the body of a request or response has content type `application/json` and consists of a well-formed JSON document, then it's JSON, otherwise not. See also [The JavaScript Object Notation (JSON) Data Interchange Format](https://www.rfc-editor.org/rfc/rfc8259). – dbc Nov 09 '21 at 15:57
  • 1
    @dbc - I learned a lot from your response here and else where on other SO posts. Thanks for your contribution to the community! – Stack Undefined Nov 11 '21 at 13:43

0 Answers0