How to Handle Enums in the Input to a .NET AWS Lambda Function

Download full source code.

If you are using the lambda.EmptyFunction template, deserialization will be performed by Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer.

This DefaultLambdaJsonSerializer cannot deserialize to enums.

For example, if you have a Car type which is the input to the function handler, it might look like this -

public class Function
{
    public string FunctionHandler(Car input, ILambdaContext context)
    {
        return input.ToString();
    }
}

public class Car
{
    public string Make { get; set; }
    public string Model { get; set; }
    public int Year { get; set; }
    public Style Style { get; set; }
    
    public override string ToString()
    {
        return $"Your car is a {Make} {Model} ({Style}), made in {Year}.";
    }
}

public enum Style
{
    Coupe,
    Hatchback,
    Sedan,
    SUV,
    Truck
}

If you invoke it with the following payload it will fail -

dotnet lambda invoke-function HandlingEnumsInLambdaFunctions --payload '{"make":"Ford","model":"Focus","Style":"Sedan","year":"2022"}'

Payload:
{
  "errorType": "JsonSerializerException",
  "errorMessage": "Error converting the Lambda event JSON payload to type HandlingEnumsInLambdaFunctions.Car: The JSON value could not be converted to HandlingEnumsInLambdaFunctions.Style. Path: $.Style | LineNumber: 0 | BytePositionInLine: 46.",

The DefaultLambdaJsonSerializer cannot handle convert "Style":"Sedan" to the Style enum.

You have two choices to fix this -

  • Create your own implementation of a serializer/deserializer and use that in your Lambda function.
  • Instead of taking a Car as the input to your function handler, take a Stream and deserialize it yourself.

Creating your own implementation of a serializer/deserializer

This is not a difficult task. Use the DefaultLambdaJsonSerializer as a base class and set your serialization options there.

     
using System.Text.Json.Serialization;
using Amazon.Lambda.Serialization.SystemTextJson;

namespace HandlingEnumsInLambdaFunctions;

public class LambdaEnumSerializer: DefaultLambdaJsonSerializer
{
    public LambdaEnumSerializer()
        : base(options => options.Converters.Add(new JsonStringEnumConverter())) { }
    
    // Same as above
    // public StringEnumSerializer()
    //     : base(ConfigureJsonSerializerOptions) { }
    //
    // private static void ConfigureJsonSerializerOptions(JsonSerializerOptions options)
    // {
    //     options.Converters.Add(new JsonStringEnumConverter());
    // }
}

Then in the Function.cs file, change the serializer to the one you just created -

[assembly: LambdaSerializer(typeof(LambdaEnumSerializer))]

Now when you invoke the function, the deserialization will work.

Taking a Stream as the input to your Lambda function

The second option is to take a stream as the input to your function handler instead of the Car type and perform the deserialization there.

public string FunctionHandler(Stream input, ILambdaContext context)
{
    var options = new JsonSerializerOptions
    {
        PropertyNameCaseInsensitive = true,
        Converters = { new JsonStringEnumConverter() },
    };

    var car = JsonSerializer.Deserialize<Car>(input, options);
    return car.ToString();
}

For better efficiency you can move var options =... outside the function handler. That way, the code is run only the first time the function is invoked.

In the attached zip file you will see that the DefaultLambdaJsonSerializer is still in place, it will be used to serialize the response back to the caller.

Download full source code.

comments powered by Disqus

Related