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 aStream
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.