Issue with Deserialization of Enum to string using System.Text.JSON
Asked Answered
J

2

15

I am currently using .NET 6 and System.Text.Json for serialization. I am having issues where system.text.json does not deserialize the enum to string properly when I return the response using OkObjectResult or ObjectResult

I have used the following on model

public class Customer
{
    public string? id { get; set; }

    public string? Name { get; set; }
    
    public string Address { get; set; }
    
    [JsonConverter(typeof(JsonStringEnumConverter))]
    public CustomerType Type {get; set;}
}

using System.Text.Json.Serialization;

[JsonConverter(typeof(JsonStringEnumConverter))]
public enum CustomerType
{
    NEW,
    EXISTING
}

Now the API Code

public async Task<IActionResult> GetCustomerById(string Id)
    {
        var results = await _customerService.GetData(Id);
        // The results have correct JSON data with enum as string but as soon as I pass this to OkObjectResult, it changes the enum back to int
        return new OkObjectResult(results );
           
    }

Service

public async Task<Customer> GetData(string Id)
    {
        var results = await _httpClient.SendAsync(Id);  // Get Data
        var jsonResults = await results.Content.ReadAsStringAsync();
       var options = new JsonSerializerOptions{ Converters ={
        new JsonStringEnumConverter()};

        return JsonSerializer.Deserialize<Customer>(jsonResults ,
            options ) ;  // This returns the data correctly
}

Now my question why OkObjectResult breaks the code and return the integer instead of enum string value

Jillian answered 6/10, 2022 at 22:3 Comment(12)
You say "The results have correct JSON data with enum as string..." but the results are actually a strongly typed Customer, meaning they aren't JSON at all: they're a C# object. The CustomerType property on that object is not a string, it's a CustomerType enum. So the question is, how is the framework choosing to serialize that C# object you're passing in? See learn.microsoft.com/en-us/aspnet/core/web-api/advanced/…Discomfortable
@Discomfortable I am not sure if I understand, but did you check the service. The results are coming from service which uses System.Text.JSON and has serializer options of JsonStringEnumConverterJillian
The service uses System.Text.JSON to deserialize the json results from the http client into a C# class (Customer). GetData doesn't return JSON: it returns a Customer.Discomfortable
@Discomfortable So what should I do? Can you helpJillian
I probably can't help much based on the information provided. Sedat's answer should have worked, unless there's some other code somewhere that's overriding that configuration. Maybe check what other converters are configured on your JsonOptions? Make sure you don't have another line of configuration code telling Web API to use the Newtonsoft serializer?Discomfortable
@Discomfortable I have checked it everywhere that there is no other configurationJillian
Might you please edit your question to share a minimal reproducible example, specifically the contents of the jsonResults string? Also, is there any chance that, in your production code, the property is actually nullable, i.e. public CustomerType? Type {get; set;}Cacomistle
@Cacomistle It is not nullable. The jsonResults are "customer":{"id":"123","type":"NEW"} So the type here is enum and it returns stringJillian
@LearnAspNet - Wait -- does jsonResults equal {"id":"123","type":"NEW"} or {"customer":{"id":"123","type":"NEW"}}? Your comment shows some sort of wrapper object (but omits the outer braces and so is technically malformed) which leaves me confused as to the precise contents of jsonResults.Cacomistle
@Cacomistle it is an example, the whole point is that it returns the type as string instead of a number. I have verified that many times and before this line of code, results are correct with string values return new OkObjectResult(results);Jillian
@LearnAspNet - honestly, without a minimal reproducible example you will be lucky to get help. Your Customer type serializes correctly as {"id":"id","Name":null,"Address":null,"Type":"NEW"} when System.Text.Json is called directly, see dotnetfiddle.net/K1LDyC. And Sedat's answer should work even when JsonStringEnumConverter is not applied directly. This leads me to believe you need to recheck your assumptions, and creating a minimal reproducible example will force you to do that,Cacomistle
That being said, you have the following problems with the JSON in your comment: 1) "customer":{"id":"123","type":"NEW"} is malformed because it lacks outer braces. 2) {"customer":{"id":"123","type":"NEW"}} has a wrapper object not reflected in you Customer data model. 3) {"id":"123","type":"NEW"} is camel cased but in your call JsonSerializer.Deserialize<Customer>(jsonResults, options ) you don't use JsonNamingPolicy.CamelCase. So as shown your ` GetData(string Id)` method should not be returning a correctly populated Customer.Cacomistle
M
8

You need to introduce the enum converter to ASP.NET in your startup code, like this:

services.AddJsonOptions(options =>
{
   var converter = new JsonStringEnumConverter();
   options.JsonSerializerOptions.Converters.Add(converter);
});
Malvin answered 6/10, 2022 at 22:36 Comment(3)
Still does not work. The response is still a numberJillian
@LearnAspNet In that case, I don't have any more ideas based on what you shared. Serialization should work perfectly with your example.Malvin
Yeah this doesn't appear to work (.Net 8). All enums still serialize as numbers.Olney
P
1

One of the reason can be related to the fact that you are registering multiple Json serialize options: Jsone & NewtonsoftJson at the same time. Keeping only one may resolve the issue. JsonStringEnumConverter

Perfusion answered 26/10, 2023 at 8:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.