最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

c# - PascalCase serialization no longer works in .NET 8System.Text.Json - Stack Overflow

programmeradmin0浏览0评论

After upgrading to .NET 8 Isolated worker from .NET 6 in-process, my API responses are all in camelCase. That is expected, but I cannot get any solution to work to return PascalCase instead.

I've tried countless solutions:

  1. Converting fully to System.Text.Json
  2. Using a custom JsonNamingPolicy to convert to PascalCase
  3. Adding a DefaultJsonTypeInfoResolver to the options
  4. Using [JsonPropertyName] which DOES work, but is not a viable solution due to there being thousands of properties in this project.

What is odd is the serializer seems to be working - JsonSerializer.Serialize(myData) results in the correct PascalCase string. BUT when returning via an OkObjectResult(myData), the JSON is always camelCase.

Please help - here is my Program.cs:

public static void Main()
{
    var host = new HostBuilder()
            .ConfigureFunctionsWebApplication((IFunctionsWorkerApplicationBuilder builder) =>
            {
                builder.Services.Configure<JsonSerializerOptions>(serializationOptions =>
                {
                    serializationOptions.PropertyNamingPolicy = new PascalCaseJsonNamingPolicy();
                    serializationOptions.TypeInfoResolver = new DefaultJsonTypeInfoResolver();
                    serializationOptions.WriteIndented = true;
                });
            })
            .ConfigureServices(services => FunctionStart.Configure(services))
            .Build();

    host.Run();
}

And my custom PascalCase policy:

public class PascalCaseJsonNamingPolicy : JsonNamingPolicy 
{
    public override string ConvertName(string name)
    {
        if (string.IsNullOrEmpty(name))
        {
            return name;
        }

        Span<char> chars = name.ToCharArray();
        chars = FixCasing(chars);
        return new string(chars);
    }

    private static Span<char> FixCasing(Span<char> chars)
    {
        if (chars.Length > 0 && char.IsLower(chars[0]))
        {
            chars[0] = char.ToUpper(chars[0]);
        }

        return chars;
    }
}

Here is a sample object that I've been testing with:

public class Document
{
    [JsonPropertyName("id")]
    public string Id { get; set; }

    public string UniqueId { get; set; }
    public string UserId { get; set; }
}

After upgrading to .NET 8 Isolated worker from .NET 6 in-process, my API responses are all in camelCase. That is expected, but I cannot get any solution to work to return PascalCase instead.

I've tried countless solutions:

  1. Converting fully to System.Text.Json
  2. Using a custom JsonNamingPolicy to convert to PascalCase
  3. Adding a DefaultJsonTypeInfoResolver to the options
  4. Using [JsonPropertyName] which DOES work, but is not a viable solution due to there being thousands of properties in this project.

What is odd is the serializer seems to be working - JsonSerializer.Serialize(myData) results in the correct PascalCase string. BUT when returning via an OkObjectResult(myData), the JSON is always camelCase.

Please help - here is my Program.cs:

public static void Main()
{
    var host = new HostBuilder()
            .ConfigureFunctionsWebApplication((IFunctionsWorkerApplicationBuilder builder) =>
            {
                builder.Services.Configure<JsonSerializerOptions>(serializationOptions =>
                {
                    serializationOptions.PropertyNamingPolicy = new PascalCaseJsonNamingPolicy();
                    serializationOptions.TypeInfoResolver = new DefaultJsonTypeInfoResolver();
                    serializationOptions.WriteIndented = true;
                });
            })
            .ConfigureServices(services => FunctionStart.Configure(services))
            .Build();

    host.Run();
}

And my custom PascalCase policy:

public class PascalCaseJsonNamingPolicy : JsonNamingPolicy 
{
    public override string ConvertName(string name)
    {
        if (string.IsNullOrEmpty(name))
        {
            return name;
        }

        Span<char> chars = name.ToCharArray();
        chars = FixCasing(chars);
        return new string(chars);
    }

    private static Span<char> FixCasing(Span<char> chars)
    {
        if (chars.Length > 0 && char.IsLower(chars[0]))
        {
            chars[0] = char.ToUpper(chars[0]);
        }

        return chars;
    }
}

Here is a sample object that I've been testing with:

public class Document
{
    [JsonPropertyName("id")]
    public string Id { get; set; }

    public string UniqueId { get; set; }
    public string UserId { get; set; }
}
Share Improve this question edited Jan 29 at 17:48 marc_s 757k184 gold badges1.4k silver badges1.5k bronze badges asked Jan 29 at 14:12 Brandon GrotheBrandon Grothe 911 silver badge7 bronze badges 11
  • 1 Might you please edit your question to share a minimal reproducible example? You've already shared your serialization options but it would help if we could see an example of a myData that reproduces the problem. Thanks! – dbc Commented Jan 29 at 14:23
  • Also, what framework and application type are you returning JSON from? I found JsonObjectSerializer in Azure.Core.Serialization and WorkerOptions in Microsoft.Azure.Functions.Worker so are you writing an Azure worker? A client? Please tag your question to clarify. Thanks! – dbc Commented Jan 29 at 14:34
  • @dbc It was a net6 in-process function, converted to net8 Isolated worker function – Brandon Grothe Commented Jan 29 at 14:35
  • And did you also switch from Json.NET to System.Text.Json? Or were you using STJ in .NET 6 also? – dbc Commented Jan 29 at 14:36
  • Yes it was originally Json.NET, and converted to System.Text to try and fix this because I thought it was an issue with both of those conflicting – Brandon Grothe Commented Jan 29 at 14:38
 |  Show 6 more comments

3 Answers 3

Reset to default 3

FINALLY I found the issue. Per Microsoft docs

ASP.NET Core has its own serialization layer, and it is not affected by customizing general serialization configuration. To customize the serialization behavior used for your HTTP triggers, you need to include an .AddMvc() call as part of service registration. The returned IMvcBuilder can be used to modify ASP.NET Core's JSON serialization settings.

Thus, leveraging the following code allowed the AspNetCore calls like OkObjectResult() to serialize to PascalCase:

var mvcBuilder = builder.Services.AddMvc().AddNewtonsoftJson();
mvcBuilder.Services.Configure<MvcNewtonsoftJsonOptions>(options =>
{
    options.SerializerSettings.ContractResolver = new DefaultContractResolver();
});

This isn't a System.Text.Json issue. JsonObjectSerializer is part of the Azure SDK. Looking at the JsonObjectSerializer.cs code on GitHub, I see that JsonPropertyName overrides the naming policy. If that attribute exists, the class will return the Name as-is, without applying the policy

private string GetPropertyName(MemberInfo memberInfo)
{
    // Mimics property name determination based on
    // https://github/dotnet/runtime/blob/dc8b6f90/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonPropertyInfo.cs#L53-L90

    JsonPropertyNameAttribute? nameAttribute = memberInfo.GetCustomAttribute<JsonPropertyNameAttribute>(false);
    if (nameAttribute != null)
    {
        return nameAttribute.Name
            ?? throw new InvalidOperationException($"The JSON property name for '{memberInfo.DeclaringType}.{memberInfo.Name}' cannot be null.");
    }
    else if (_options.PropertyNamingPolicy != null)
    {
        return _options.PropertyNamingPolicy.ConvertName(memberInfo.Name)
            ?? throw new InvalidOperationException($"The JSON property name for '{memberInfo.DeclaringType}.{memberInfo.Name}' cannot be null.");
    }
    else
    {
        return memberInfo.Name;
    }
}

If you want to avoid this you should rename Chunks to Documents and remove JsonPropertyName

I am able to send response in pascal case using below code:

Function.cs:

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.Logging;
using System.Text.Json;

namespace RithApp1
{
    public class Function1
    {
        private readonly ILogger<Function1> ri_lg;
        public Function1(ILogger<Function1> rilg)
        {
            ri_lg = rilg;
        }
        [Function("Function1")]
        public IActionResult Run([HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequest req)
        {
            ri_lg.LogInformation("Hello Rithwik, Function execeution started");
            var test = new
            {
                Id = 8,
                UserName = "Rithwik Bojja",
                Email = "[email protected]"
            };
            string ri_out = JsonSerializer.Serialize(test);
            return new OkObjectResult(ri_out);
        }
    }
}

Program.cs:

using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System.Text.Json;

public class Program
{
    public static void Main()
    {
        var cho = new HostBuilder()
            .ConfigureFunctionsWebApplication()
            .ConfigureServices(rith =>
            {
                rith
                    .AddHttpClient() 
                    .Configure<JsonSerializerOptions>(options =>
                    {
                        options.PropertyNamingPolicy = null;
                        options.PropertyNameCaseInsensitive = true;
                    });
                })
                    .Build();

        cho.Run();
    }
}

Output:

发布评论

评论列表(0)

  1. 暂无评论