Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to configure a default JsonSerializerOptions (System.Text.Json) to be used by Azure Function v3?

I have a set of Azure Functions v3 running on .net core 3.1.

I have a custom configuration of JsonSerializerOptions that I want to be used automatically by my functions when de/serializing data.

Question

How can I set up my Azure Functions so that they can use my default System.Text.Json.JsonSerializerOptions instance?

Update 1

Following the suggestion from @sellotape, I found out the following documentation regarding the JsonResult class:

JsonResult documentation

The problem is that my JsonResult instance does not have this property of type object ; it is only accepting a JsonSerializerSettings instance.

Update 2

I still get the following error and I am not sure where Newtonsoft is coming from:

Microsoft.AspNetCore.Mvc.NewtonsoftJson: Property 'JsonResult.SerializerSettings' must be an instance of type 'Newtonsoft.Json.JsonSerializerSettings'.
like image 569
Kzrystof Avatar asked Mar 07 '20 04:03

Kzrystof


2 Answers

It's not a perfect solution for those with existing Azure Function apps, but the Azure Functions for .net 5 isolated runtime allows you to configure the JSON serialization options now. So for anyone starting new azure functions or have the budget and energy to upgrade existing ones, there's now first-class support for configuring the serializer exactly how you like it. It's unfortunate that enums have been second class citizens until just now, but better late than never.

Program.cs

using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.DependencyInjection;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace MyFunctionApp
{
    public class Program
    {
        public static void Main()
        {
            var host = new HostBuilder()
                .ConfigureFunctionsWorkerDefaults()
                .ConfigureServices(services =>
                {
                    services.Configure<JsonSerializerOptions>(options => 
                    {
                        options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
                        options.Converters.Add(new JsonStringEnumConverter());
                    });
                })
                .Build();

            host.Run();
        }
    }
}

If you want to pick System.Text.Json or Newtonsoft.Json there you could configure either one like seen in this example

// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

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

namespace Configuration
{
    public class Program
    {
        public static void Main()
        {
            var host = new HostBuilder()
                .ConfigureFunctionsWorkerDefaults(workerApplication =>
                {
                    // Use any of the extension methods in WorkerConfigurationExtensions.
                })
                .Build();

            host.Run();
        }
    }

    internal static class WorkerConfigurationExtensions
    {
        /// <summary>
        /// Calling ConfigureFunctionsWorkerDefaults() configures the Functions Worker to use System.Text.Json for all JSON
        /// serialization and sets JsonSerializerOptions.PropertyNameCaseInsensitive = true;
        /// This method uses DI to modify the JsonSerializerOptions. Call /api/HttpFunction to see the changes.
        /// </summary>
        public static IFunctionsWorkerApplicationBuilder ConfigureSystemTextJson(this IFunctionsWorkerApplicationBuilder builder)
        {
            builder.Services.Configure<JsonSerializerOptions>(jsonSerializerOptions =>
            {
                jsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
                jsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
                jsonSerializerOptions.ReferenceHandler = ReferenceHandler.Preserve;

                // override the default value
                jsonSerializerOptions.PropertyNameCaseInsensitive = false;
            });

            return builder;
        }

        /// <summary>
        /// The functions worker uses the Azure SDK's ObjectSerializer to abstract away all JSON serialization. This allows you to
        /// swap out the default System.Text.Json implementation for the Newtonsoft.Json implementation.
        /// To do so, add the Microsoft.Azure.Core.NewtonsoftJson nuget package and then update the WorkerOptions.Serializer property.
        /// This method updates the Serializer to use Newtonsoft.Json. Call /api/HttpFunction to see the changes.
        /// </summary>
        public static IFunctionsWorkerApplicationBuilder UseNewtonsoftJson(this IFunctionsWorkerApplicationBuilder builder)
        {
            builder.Services.Configure<WorkerOptions>(workerOptions =>
            {
                var settings = NewtonsoftJsonObjectSerializer.CreateJsonSerializerSettings();
                settings.ContractResolver = new CamelCasePropertyNamesContractResolver();
                settings.NullValueHandling = NullValueHandling.Ignore;

                workerOptions.Serializer = new NewtonsoftJsonObjectSerializer(settings);
            });

            return builder;
        }
    }
}
like image 186
Tyson Avatar answered Oct 11 '22 02:10

Tyson


Turns out I missed a step when upgrading my Azure Functions from v2 to v3. To make it work, I had to add the Microsoft.AspNetCore.App framework to my csproj. Otherwise, my project kept referencing JsonResult from Microsoft.Aspnet.Mvc.Core v2.X.X which does not support System.Text.Json.

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netcoreapp3.0</TargetFramework>
  </PropertyGroup>

  <!-- ADD THESE LINES -->
  <ItemGroup>
    <FrameworkReference Include="Microsoft.AspNetCore.App" />
  </ItemGroup>

</Project>

Then, I was able to specify my own JsonSerializerOptions instance like this:

return new JsonResult(<ContractClass>, NSJsonSerializerOptions.Default)
{
    StatusCode = StatusCodes.Status200OK
};

where NSJsonSerializerOptions.Default is a static instance of JsonSerializerOptions.

like image 3
Kzrystof Avatar answered Oct 11 '22 01:10

Kzrystof