AWS Lambda with Annotations: JSON serializer is not used

99 Views Asked by At

Since the default JSON serializer for AWS SDK .net serializes properties into PascalCase and I want camelCase, I created a custom serializer:

public class WellBehavedJsonSerializer: DefaultLambdaJsonSerializer {

    public WellBehavedJsonSerializer() : base(CustomizeSerializer) { }

    private static void CustomizeSerializer(JsonSerializerOptions options) {
        options.DictionaryKeyPolicy = JsonNamingPolicy.CamelCase;
        options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
        options.PropertyNameCaseInsensitive = true;
        options.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
        options.Converters.Add(new JsonStringEnumConverter(JsonNamingPolicy.CamelCase));
    }
}

I put this to the top of my Functions.cs:

[assembly: LambdaSerializer(typeof(WellBehavedJsonSerializer))]

But it is still serializing to PascalCase:

[
  {
    "Item": {
      "ContainerID": "682b9bee-3ac4-4f8d-a768-b350e7a33b39",
      "ItemID": "47147426-8948-4410-9486-196c6033f495",
      "Name": "bar",
      "Description": "",
      "Taken": false,
      "ReturnDue": "0001-01-01T00:00:00+00:00"
    },
    "Containers": [
      {
        "ContainerID": "682b9bee-3ac4-4f8d-a768-b350e7a33b39",
        "Name": "foo",
        "ParentContainerId": "00000000-0000-0000-0000-000000000000"
      }
    ]
  }
]

Is there anything else what I might be missing? Add some extra configuration to Startup.cs or the like?

Followup 1

There is a CamelCaseLambdaJsonSerializer but when I use this, I still get PascalCase.

2

There are 2 best solutions below

0
Ankush Jain On

Root cause

The reason it is not working as expected is because the .NET Lambda Annotations framework generates code for your Lambda function automatically in the background.

You can take a look at that auto-generated code at the following path.

Dependencies > Analyzers > Amazon.Lambda.Annotations.SourceGenerator

In that auto-generated code, Lambda uses System.Text.Json.JsonSerializer.Serialize irrespective of what you define in assembly attribute.

enter image description here

What if you don't use Lambda Annotations framework

Now, let's talk about what if you don't use this framework, and write a normal Lambda function in .NET. You would use APIGatewayProxyResponse object to return the response.

public APIGatewayProxyResponse FunctionHandler(APIGatewayProxyRequest input, ILambdaContext context)
{
    return new APIGatewayProxyResponse
    {
        StatusCode = 200,
        Body =  JsonSerializer.Serialize(new Customer())
    };
}

If you notice, here the body property is string, and it's your responsibility to serialize the object in whatever case (Camel or Pascal).

The chosen Lambda serializer will be applicable on the serialization of APIGatewayProxyResponse object, not on the internal body property.

Notice, here the type of body is string.

enter image description here

Conclusion and Solution

I understand, that with Lambda Annotations, you really don't have much control over how response for body is being serialized, but at least you know the reason now, why it happens.

But, as a workaround, you can always use APIGatewayProxyResponse object as return type in your function when working with Lambda Annotations Framework.

This way, you'll have the following benefits.

  • More control over the serialization of body property.
  • More control over status code and headers.
[Logging]
[LambdaFunction(ResourceName = "SearchUsersHandler", MemorySize = 1024, PackageType = LambdaPackageType.Zip)]
[RestApi(LambdaHttpMethod.Get, "/search-users/{searchText}")]
public async Task<APIGatewayProxyResponse> SearchUsersHandler(string searchText, ILambdaContext context)
{
    try
    {
        ...
        ...

        return APIGatewayResponseManager.ToAPIGatewaySuccessResponse<SearchUsersResponseDto>(response);
    }
    catch (Exception e)
    {
        return APIGatewayResponseManager.ToAPIGatewayeErrorResponse(e);
    }
}

Note: In the above code APIGatewayResponseManager is a self maintained wrapper class, not part of .NET SDK.

0
Norm Johanson On

This was fixed in version 1.1 of Amazon.Lambda.Annotations to use the registered LambdaSerializer for all of its serialization needs. You should just need to update the package version. Given it is a source generator you might need to restart Visual Studio to see the change in VS.

We will be updating the template soon in Visual Studio to use the latest version of Amazon.Lambda.Annotations with the upcoming Lambda support for .NET 8.