diff --git a/Libraries/src/Amazon.Lambda.ApplicationLoadBalancerEvents/Amazon.Lambda.ApplicationLoadBalancerEvents.csproj b/Libraries/src/Amazon.Lambda.ApplicationLoadBalancerEvents/Amazon.Lambda.ApplicationLoadBalancerEvents.csproj index 70b47b4cf..57571f15a 100644 --- a/Libraries/src/Amazon.Lambda.ApplicationLoadBalancerEvents/Amazon.Lambda.ApplicationLoadBalancerEvents.csproj +++ b/Libraries/src/Amazon.Lambda.ApplicationLoadBalancerEvents/Amazon.Lambda.ApplicationLoadBalancerEvents.csproj @@ -3,10 +3,10 @@ - netstandard2.0 + netstandard2.0;netcoreapp3.1 Amazon Lambda .NET Core support - Application Load Balancer package. Amazon.Lambda.ApplicationLoadBalancerEvents - 1.0.0 + 2.0.0 Amazon.Lambda.ApplicationLoadBalancerEvents Amazon.Lambda.ApplicationLoadBalancerEvents AWS;Amazon;Lambda diff --git a/Libraries/src/Amazon.Lambda.ApplicationLoadBalancerEvents/ApplicationLoadBalancerResponse.cs b/Libraries/src/Amazon.Lambda.ApplicationLoadBalancerEvents/ApplicationLoadBalancerResponse.cs index 2307ac388..0410fe584 100644 --- a/Libraries/src/Amazon.Lambda.ApplicationLoadBalancerEvents/ApplicationLoadBalancerResponse.cs +++ b/Libraries/src/Amazon.Lambda.ApplicationLoadBalancerEvents/ApplicationLoadBalancerResponse.cs @@ -14,12 +14,18 @@ public class ApplicationLoadBalancerResponse /// The HTTP status code for the request /// [DataMember(Name = "statusCode")] +#if NETCOREAPP3_1 + [System.Text.Json.Serialization.JsonPropertyName("statusCode")] +#endif public int StatusCode { get; set; } /// /// The HTTP status description for the request /// [DataMember(Name = "statusDescription")] +#if NETCOREAPP3_1 + [System.Text.Json.Serialization.JsonPropertyName("statusDescription")] +#endif public string StatusDescription { get; set; } /// @@ -27,6 +33,9 @@ public class ApplicationLoadBalancerResponse /// Note: Use this property when "Multi value headers" is disabled on ELB Target Group. /// [DataMember(Name = "headers")] +#if NETCOREAPP3_1 + [System.Text.Json.Serialization.JsonPropertyName("headers")] +#endif public IDictionary Headers { get; set; } /// @@ -34,18 +43,27 @@ public class ApplicationLoadBalancerResponse /// Note: Use this property when "Multi value headers" is enabled on ELB Target Group. /// [DataMember(Name = "multiValueHeaders")] +#if NETCOREAPP3_1 + [System.Text.Json.Serialization.JsonPropertyName("multiValueHeaders")] +#endif public IDictionary> MultiValueHeaders { get; set; } /// /// The response body /// [DataMember(Name = "body")] +#if NETCOREAPP3_1 + [System.Text.Json.Serialization.JsonPropertyName("body")] +#endif public string Body { get; set; } /// /// Flag indicating whether the body should be treated as a base64-encoded string /// [DataMember(Name = "isBase64Encoded")] +#if NETCOREAPP3_1 + [System.Text.Json.Serialization.JsonPropertyName("isBase64Encoded")] +#endif public bool IsBase64Encoded { get; set; } } } \ No newline at end of file diff --git a/Libraries/src/Amazon.Lambda.AspNetCoreServer/AbstractAspNetCoreFunction.cs b/Libraries/src/Amazon.Lambda.AspNetCoreServer/AbstractAspNetCoreFunction.cs index 5a742c462..71e7ab582 100644 --- a/Libraries/src/Amazon.Lambda.AspNetCoreServer/AbstractAspNetCoreFunction.cs +++ b/Libraries/src/Amazon.Lambda.AspNetCoreServer/AbstractAspNetCoreFunction.cs @@ -343,7 +343,7 @@ protected string ErrorReport(Exception e) #if NETCOREAPP_2_1 [LambdaSerializer(typeof(Amazon.Lambda.Serialization.Json.JsonSerializer))] #elif NETCOREAPP_3_1 - [LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.LambdaJsonSerializer))] + [LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] #endif public virtual async Task FunctionHandlerAsync(TREQUEST request, ILambdaContext lambdaContext) { diff --git a/Libraries/src/Amazon.Lambda.KinesisAnalyticsEvents/Amazon.Lambda.KinesisAnalyticsEvents.csproj b/Libraries/src/Amazon.Lambda.KinesisAnalyticsEvents/Amazon.Lambda.KinesisAnalyticsEvents.csproj index c71c2527d..c404be27f 100644 --- a/Libraries/src/Amazon.Lambda.KinesisAnalyticsEvents/Amazon.Lambda.KinesisAnalyticsEvents.csproj +++ b/Libraries/src/Amazon.Lambda.KinesisAnalyticsEvents/Amazon.Lambda.KinesisAnalyticsEvents.csproj @@ -6,17 +6,10 @@ netstandard2.0;netcoreapp3.1 Amazon Lambda .NET Core support - Amazon Kinesis Analytics package. Amazon.Lambda.KinesisAnalyticsEvents - 2.0.0 + 2.1.0 Amazon.Lambda.KinesisAnalyticsEvents Amazon.Lambda.KinesisAnalyticsEvents AWS;Amazon;Lambda;KinesisAnalytics - - - NETCOREAPP_3_1 - - - - - + diff --git a/Libraries/src/Amazon.Lambda.KinesisAnalyticsEvents/KinesisAnalyticsInputPreprocessingResponse.cs b/Libraries/src/Amazon.Lambda.KinesisAnalyticsEvents/KinesisAnalyticsInputPreprocessingResponse.cs index 7bbd62324..15f66a06f 100644 --- a/Libraries/src/Amazon.Lambda.KinesisAnalyticsEvents/KinesisAnalyticsInputPreprocessingResponse.cs +++ b/Libraries/src/Amazon.Lambda.KinesisAnalyticsEvents/KinesisAnalyticsInputPreprocessingResponse.cs @@ -33,8 +33,8 @@ public class KinesisAnalyticsInputPreprocessingResponse /// The records. /// [DataMember(Name = "records")] -#if NETCOREAPP_3_1 - [System.Text.Json.Serialization.JsonPropertyName("records")] +#if NETCOREAPP3_1 + [System.Text.Json.Serialization.JsonPropertyName("records")] #endif public IList Records { get; set; } @@ -51,7 +51,7 @@ public class Record /// The record identifier. /// [DataMember(Name = "recordId")] -#if NETCOREAPP_3_1 +#if NETCOREAPP3_1 [System.Text.Json.Serialization.JsonPropertyName("recordId")] #endif public string RecordId { get; set; } @@ -63,7 +63,7 @@ public class Record /// The result. /// [DataMember(Name = "result")] -#if NETCOREAPP_3_1 +#if NETCOREAPP3_1 [System.Text.Json.Serialization.JsonPropertyName("result")] #endif public string Result { get; set; } @@ -75,7 +75,7 @@ public class Record /// The base64 encoded data. /// [DataMember(Name = "data")] -#if NETCOREAPP_3_1 +#if NETCOREAPP3_1 [System.Text.Json.Serialization.JsonPropertyName("data")] #endif public string Base64EncodedData { get; set; } diff --git a/Libraries/src/Amazon.Lambda.KinesisAnalyticsEvents/KinesisAnalyticsOutputDeliveryEvent.cs b/Libraries/src/Amazon.Lambda.KinesisAnalyticsEvents/KinesisAnalyticsOutputDeliveryEvent.cs index bf9eda67b..4cf7f51ab 100644 --- a/Libraries/src/Amazon.Lambda.KinesisAnalyticsEvents/KinesisAnalyticsOutputDeliveryEvent.cs +++ b/Libraries/src/Amazon.Lambda.KinesisAnalyticsEvents/KinesisAnalyticsOutputDeliveryEvent.cs @@ -85,7 +85,7 @@ public class LambdaDeliveryRecordMetadata /// The base64 encoded data. /// [DataMember(Name = "data")] -#if NETCOREAPP_3_1 +#if NETCOREAPP3_1 [System.Text.Json.Serialization.JsonPropertyName("data")] #endif public string Base64EncodedData { get; set; } diff --git a/Libraries/src/Amazon.Lambda.KinesisAnalyticsEvents/KinesisAnalyticsOutputDeliveryResponse.cs b/Libraries/src/Amazon.Lambda.KinesisAnalyticsEvents/KinesisAnalyticsOutputDeliveryResponse.cs index 4b10026aa..0f6deca92 100644 --- a/Libraries/src/Amazon.Lambda.KinesisAnalyticsEvents/KinesisAnalyticsOutputDeliveryResponse.cs +++ b/Libraries/src/Amazon.Lambda.KinesisAnalyticsEvents/KinesisAnalyticsOutputDeliveryResponse.cs @@ -28,6 +28,9 @@ public class KinesisAnalyticsOutputDeliveryResponse /// The records. /// [DataMember(Name = "records")] +#if NETCOREAPP3_1 + [System.Text.Json.Serialization.JsonPropertyName("records")] +#endif public IList Records { get; set; } /// @@ -43,6 +46,9 @@ public class Record /// The record identifier. /// [DataMember(Name = "recordId")] +#if NETCOREAPP3_1 + [System.Text.Json.Serialization.JsonPropertyName("recordId")] +#endif public string RecordId { get; set; } /// @@ -52,6 +58,9 @@ public class Record /// The result. /// [DataMember(Name = "result")] +#if NETCOREAPP3_1 + [System.Text.Json.Serialization.JsonPropertyName("result")] +#endif public string Result { get; set; } } } diff --git a/Libraries/src/Amazon.Lambda.KinesisAnalyticsEvents/KinesisAnalyticsStreamsInputPreprocessingEvent.cs b/Libraries/src/Amazon.Lambda.KinesisAnalyticsEvents/KinesisAnalyticsStreamsInputPreprocessingEvent.cs index 15e4ba7d0..6114f5db0 100644 --- a/Libraries/src/Amazon.Lambda.KinesisAnalyticsEvents/KinesisAnalyticsStreamsInputPreprocessingEvent.cs +++ b/Libraries/src/Amazon.Lambda.KinesisAnalyticsEvents/KinesisAnalyticsStreamsInputPreprocessingEvent.cs @@ -131,7 +131,7 @@ public DateTime ApproximateArrivalTimestamp /// The base64 encoded data. /// [DataMember(Name = "data")] -#if NETCOREAPP_3_1 +#if NETCOREAPP3_1 [System.Text.Json.Serialization.JsonPropertyName("data")] #endif public string Base64EncodedData { get; set; } diff --git a/Libraries/src/Amazon.Lambda.KinesisFirehoseEvents/KinesisFirehoseResponse.cs b/Libraries/src/Amazon.Lambda.KinesisFirehoseEvents/KinesisFirehoseResponse.cs index 7bd333ff2..76d9fcb81 100644 --- a/Libraries/src/Amazon.Lambda.KinesisFirehoseEvents/KinesisFirehoseResponse.cs +++ b/Libraries/src/Amazon.Lambda.KinesisFirehoseEvents/KinesisFirehoseResponse.cs @@ -32,6 +32,9 @@ public class KinesisFirehoseResponse /// The transformed records from the KinesisFirehoseEvent. /// [DataMember(Name = "records")] +#if NETCOREAPP_3_1 + [System.Text.Json.Serialization.JsonPropertyName("records")] +#endif public IList Records { get; set; } /// @@ -46,6 +49,9 @@ public class FirehoseRecord ///transformed record is treated as a data transformation failure. /// [DataMember(Name = "recordId")] +#if NETCOREAPP_3_1 + [System.Text.Json.Serialization.JsonPropertyName("recordId")] +#endif public string RecordId { get; set; } /// @@ -72,6 +78,9 @@ public class FirehoseRecord /// /// [DataMember(Name = "result")] +#if NETCOREAPP_3_1 + [System.Text.Json.Serialization.JsonPropertyName("result")] +#endif public string Result { get; set; } /// diff --git a/Libraries/src/Amazon.Lambda.LexEvents/Amazon.Lambda.LexEvents.csproj b/Libraries/src/Amazon.Lambda.LexEvents/Amazon.Lambda.LexEvents.csproj index a8e81802e..e334808b9 100644 --- a/Libraries/src/Amazon.Lambda.LexEvents/Amazon.Lambda.LexEvents.csproj +++ b/Libraries/src/Amazon.Lambda.LexEvents/Amazon.Lambda.LexEvents.csproj @@ -4,16 +4,12 @@ Amazon Lambda .NET Core support - Amazon Lex package. - netstandard1.3;netstandard2.0 + netstandard2.0;netcoreapp3.1 Amazon.Lambda.LexEvents - 1.1.0 + 2.0.0 Amazon.Lambda.LexEvents Amazon.Lambda.LexEvents AWS;Amazon;Lambda;Lex - - - - \ No newline at end of file diff --git a/Libraries/src/Amazon.Lambda.LexEvents/LexResponse.cs b/Libraries/src/Amazon.Lambda.LexEvents/LexResponse.cs index 9dc8d6b57..41fc1f956 100644 --- a/Libraries/src/Amazon.Lambda.LexEvents/LexResponse.cs +++ b/Libraries/src/Amazon.Lambda.LexEvents/LexResponse.cs @@ -15,6 +15,9 @@ public class LexResponse /// Application-specific session attributes. This is an optional field. /// [DataMember(Name = "sessionAttributes", EmitDefaultValue=false)] +#if NETCOREAPP3_1 + [System.Text.Json.Serialization.JsonPropertyName("sessionAttributes")] +#endif public IDictionary SessionAttributes { get; set; } /// @@ -23,6 +26,9 @@ public class LexResponse /// after Amazon Lex returns a response to the client. /// \ [DataMember(Name = "dialogAction", EmitDefaultValue=false)] +#if NETCOREAPP3_1 + [System.Text.Json.Serialization.JsonPropertyName("dialogAction")] +#endif public LexDialogAction DialogAction { get; set; } /// @@ -35,42 +41,63 @@ public class LexDialogAction /// The type of action for Lex to take with the response from the Lambda function. /// [DataMember(Name = "type", EmitDefaultValue=false)] +#if NETCOREAPP3_1 + [System.Text.Json.Serialization.JsonPropertyName("type")] +#endif public string Type { get; set; } /// /// The state of the fullfillment. "Fulfilled" or "Failed" /// [DataMember(Name = "fulfillmentState", EmitDefaultValue=false)] +#if NETCOREAPP3_1 + [System.Text.Json.Serialization.JsonPropertyName("fulfillmentState")] +#endif public string FulfillmentState { get; set; } /// /// The message to be sent to the user. /// [DataMember(Name = "message", EmitDefaultValue=false)] +#if NETCOREAPP3_1 + [System.Text.Json.Serialization.JsonPropertyName("message")] +#endif public LexMessage Message { get; set; } /// /// The intent name you want to confirm or elicit. /// [DataMember(Name = "intentName", EmitDefaultValue=false)] +#if NETCOREAPP3_1 + [System.Text.Json.Serialization.JsonPropertyName("intentName")] +#endif public string IntentName { get; set; } /// /// The values for all of the slots when response is of type "Delegate". /// [DataMember(Name = "slots", EmitDefaultValue=false)] +#if NETCOREAPP3_1 + [System.Text.Json.Serialization.JsonPropertyName("slots")] +#endif public IDictionary Slots { get; set; } /// /// The slot to elicit when the Type is "ElicitSlot" /// [DataMember(Name = "slotToElicit", EmitDefaultValue=false)] +#if NETCOREAPP3_1 + [System.Text.Json.Serialization.JsonPropertyName("slotToElicit")] +#endif public string SlotToElicit { get; set; } /// /// The response card provides information back to the bot to display for the user. /// [DataMember(Name = "responseCard", EmitDefaultValue=false)] +#if NETCOREAPP3_1 + [System.Text.Json.Serialization.JsonPropertyName("responseCard")] +#endif public LexResponseCard ResponseCard { get; set; } } @@ -84,12 +111,18 @@ public class LexMessage /// The content type of the message. PlainText or SSML /// [DataMember(Name = "contentType", EmitDefaultValue=false)] +#if NETCOREAPP3_1 + [System.Text.Json.Serialization.JsonPropertyName("contentType")] +#endif public string ContentType { get; set; } /// /// The message to be asked to the user by the bot. /// [DataMember(Name = "content", EmitDefaultValue=false)] +#if NETCOREAPP3_1 + [System.Text.Json.Serialization.JsonPropertyName("content")] +#endif public string Content { get; set; } } @@ -103,18 +136,27 @@ public class LexResponseCard /// The version of the response card. /// [DataMember(Name = "version", EmitDefaultValue=false)] +#if NETCOREAPP3_1 + [System.Text.Json.Serialization.JsonPropertyName("version")] +#endif public int? Version { get; set; } /// /// The content type of the response card. The default is "application/vnd.amazonaws.card.generic". /// [DataMember(Name = "contentType", EmitDefaultValue=false)] +#if NETCOREAPP3_1 + [System.Text.Json.Serialization.JsonPropertyName("contentType")] +#endif public string ContentType { get; set; } = "application/vnd.amazonaws.card.generic"; /// /// The list of attachments sent back with the response card. /// [DataMember(Name = "genericAttachments", EmitDefaultValue=false)] +#if NETCOREAPP3_1 + [System.Text.Json.Serialization.JsonPropertyName("genericAttachments")] +#endif public IList GenericAttachments { get; set; } } @@ -128,30 +170,45 @@ public class LexGenericAttachments /// The card's title. /// [DataMember(Name = "title", EmitDefaultValue=false)] +#if NETCOREAPP3_1 + [System.Text.Json.Serialization.JsonPropertyName("title")] +#endif public string Title { get; set; } /// /// The card's sub title. /// [DataMember(Name = "subTitle", EmitDefaultValue=false)] +#if NETCOREAPP3_1 + [System.Text.Json.Serialization.JsonPropertyName("subTitle")] +#endif public string SubTitle { get; set; } /// /// URL to an image to be shown. /// [DataMember(Name = "imageUrl", EmitDefaultValue=false)] +#if NETCOREAPP3_1 + [System.Text.Json.Serialization.JsonPropertyName("imageUrl")] +#endif public string ImageUrl { get; set; } /// /// URL of the attachment to be associated with the card. /// [DataMember(Name = "attachmentLinkUrl", EmitDefaultValue=false)] +#if NETCOREAPP3_1 + [System.Text.Json.Serialization.JsonPropertyName("attachmentLinkUrl")] +#endif public string AttachmentLinkUrl { get; set; } /// /// The list of buttons to be displayed with the response card. /// [DataMember(Name = "buttons", EmitDefaultValue=false)] +#if NETCOREAPP3_1 + [System.Text.Json.Serialization.JsonPropertyName("buttons")] +#endif public IList Buttons { get; set; } } @@ -165,12 +222,18 @@ public class LexButton /// The text for the button. /// [DataMember(Name = "text", EmitDefaultValue=false)] +#if NETCOREAPP3_1 + [System.Text.Json.Serialization.JsonPropertyName("text")] +#endif public string Text { get; set; } /// /// The value of the button sent back to the server. /// [DataMember(Name = "value", EmitDefaultValue=false)] +#if NETCOREAPP3_1 + [System.Text.Json.Serialization.JsonPropertyName("value")] +#endif public string Value { get; set; } } } diff --git a/Libraries/src/Amazon.Lambda.Serialization.SystemTextJson/Amazon.Lambda.Serialization.SystemTextJson.csproj b/Libraries/src/Amazon.Lambda.Serialization.SystemTextJson/Amazon.Lambda.Serialization.SystemTextJson.csproj index a93b046ad..7fca70b77 100644 --- a/Libraries/src/Amazon.Lambda.Serialization.SystemTextJson/Amazon.Lambda.Serialization.SystemTextJson.csproj +++ b/Libraries/src/Amazon.Lambda.Serialization.SystemTextJson/Amazon.Lambda.Serialization.SystemTextJson.csproj @@ -9,7 +9,7 @@ Amazon.Lambda.Serialization.SystemTextJson Amazon.Lambda.Serialization.SystemTextJson AWS;Amazon;Lambda - 1.0.0 + 2.0.0 diff --git a/Libraries/src/Amazon.Lambda.Serialization.SystemTextJson/AwsNamingPolicy.cs b/Libraries/src/Amazon.Lambda.Serialization.SystemTextJson/AwsNamingPolicy.cs index 6cbbc7b1a..3a86249b9 100644 --- a/Libraries/src/Amazon.Lambda.Serialization.SystemTextJson/AwsNamingPolicy.cs +++ b/Libraries/src/Amazon.Lambda.Serialization.SystemTextJson/AwsNamingPolicy.cs @@ -15,6 +15,27 @@ public class AwsNamingPolicy : JsonNamingPolicy {"XAmzRequestId", "x-amz-request-id" } }; + private readonly JsonNamingPolicy _fallbackNamingPolicy; + + /// + /// Creates the AWS Naming policy. If the name matches one of the reserved AWS words it will return the + /// appropriate mapping for it. Otherwise the name will be returned as is like the JsonDefaultNamingPolicy. + /// + public AwsNamingPolicy() + { + + } + + /// + /// Creates the AWS Naming policy. If the name matches one of the reserved AWS words it will return the + /// appropriate mapping for it. Otherwise the JsonNamingPolicy passed in will be used to map the name. + /// + /// + public AwsNamingPolicy(JsonNamingPolicy fallbackNamingPolicy) + { + _fallbackNamingPolicy = fallbackNamingPolicy; + } + /// /// Map names that don't camel case. /// @@ -27,7 +48,9 @@ public override string ConvertName(string name) return mapNamed; } - return JsonNamingPolicy.CamelCase.ConvertName(name); + // If no naming policy given then just return the name like the JsonDefaultNamingPolicy policy. + // https://github.com/dotnet/runtime/blob/master/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonDefaultNamingPolicy.cs + return _fallbackNamingPolicy?.ConvertName(name) ?? name; } } } diff --git a/Libraries/src/Amazon.Lambda.Serialization.SystemTextJson/CamelCaseLambdaJsonSerializer.cs b/Libraries/src/Amazon.Lambda.Serialization.SystemTextJson/CamelCaseLambdaJsonSerializer.cs new file mode 100644 index 000000000..3f9eae8db --- /dev/null +++ b/Libraries/src/Amazon.Lambda.Serialization.SystemTextJson/CamelCaseLambdaJsonSerializer.cs @@ -0,0 +1,33 @@ +using System.Text.Json; + +namespace Amazon.Lambda.Serialization.SystemTextJson +{ + /// + /// Custom ILambdaSerializer implementation which uses System.Text.Json + /// for serialization. + /// + /// + /// When serializing objects to JSON camel casing will be used for JSON property names. + /// + /// + /// If the environment variable LAMBDA_NET_SERIALIZER_DEBUG is set to true the JSON coming + /// in from Lambda and being sent back to Lambda will be logged. + /// + /// + public class CamelCaseLambdaJsonSerializer : DefaultLambdaJsonSerializer + { + /// + /// Constructs instance of serializer. + /// + public CamelCaseLambdaJsonSerializer() + : base(ConfigureJsonSerializerOptions) + { + + } + + private static void ConfigureJsonSerializerOptions(JsonSerializerOptions options) + { + options.PropertyNamingPolicy = new AwsNamingPolicy(JsonNamingPolicy.CamelCase); + } + } +} \ No newline at end of file diff --git a/Libraries/src/Amazon.Lambda.Serialization.SystemTextJson/DefaultLambdaJsonSerializer.cs b/Libraries/src/Amazon.Lambda.Serialization.SystemTextJson/DefaultLambdaJsonSerializer.cs new file mode 100644 index 000000000..ddb721fe2 --- /dev/null +++ b/Libraries/src/Amazon.Lambda.Serialization.SystemTextJson/DefaultLambdaJsonSerializer.cs @@ -0,0 +1,157 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Text.Json; +using Amazon.Lambda.Core; +using Amazon.Lambda.Serialization.SystemTextJson.Converters; + +namespace Amazon.Lambda.Serialization.SystemTextJson +{ + /// + /// Custom ILambdaSerializer implementation which uses System.Text.Json + /// for serialization. + /// + /// + /// If the environment variable LAMBDA_NET_SERIALIZER_DEBUG is set to true the JSON coming + /// in from Lambda and being sent back to Lambda will be logged. + /// + /// + public class DefaultLambdaJsonSerializer : ILambdaSerializer + { + private const string DEBUG_ENVIRONMENT_VARIABLE_NAME = "LAMBDA_NET_SERIALIZER_DEBUG"; + private readonly bool _debug; + + /// + /// The options used to serialize JSON object. + /// + protected JsonSerializerOptions SerializerOptions { get; } + + /// + /// Constructs instance of serializer. + /// + public DefaultLambdaJsonSerializer() + { + SerializerOptions = new JsonSerializerOptions() + { + IgnoreNullValues = true, + PropertyNameCaseInsensitive = true, + PropertyNamingPolicy = new AwsNamingPolicy(), + Converters = + { + new DateTimeConverter(), + new MemoryStreamConverter(), + new ConstantClassConverter() + } + }; + + this._debug = string.Equals(Environment.GetEnvironmentVariable(DEBUG_ENVIRONMENT_VARIABLE_NAME), "true", + StringComparison.OrdinalIgnoreCase); + } + + /// + /// Constructs instance of serializer with the option to customize the JsonSerializerOptions after the + /// Amazon.Lambda.Serialization.SystemTextJson's default settings have been applied. + /// + /// + public DefaultLambdaJsonSerializer(Action customizer) + : this() + { + customizer?.Invoke(this.SerializerOptions); + } + + /// + /// Serializes a particular object to a stream. + /// + /// Type of object to serialize. + /// Object to serialize. + /// Output stream. + public void Serialize(T response, Stream responseStream) + { + try + { + if (_debug) + { + using (var debugWriter = new StringWriter()) + using (var utf8Writer = new Utf8JsonWriter(responseStream)) + { + JsonSerializer.Serialize(utf8Writer, response, SerializerOptions); + + var jsonDocument = debugWriter.ToString(); + Console.WriteLine($"Lambda Serialize {response.GetType().FullName}: {jsonDocument}"); + + var writer = new StreamWriter(responseStream); + writer.Write(jsonDocument); + writer.Flush(); + } + } + else + { + using (var writer = new Utf8JsonWriter(responseStream)) + { + JsonSerializer.Serialize(writer, response, SerializerOptions); + } + } + } + catch(Exception e) + { + throw new JsonSerializerException($"Error converting the response object of type {typeof(T).FullName} from the Lambda function to JSON: {e.Message}", e); + } + } + + /// + /// Deserializes a stream to a particular type. + /// + /// Type of object to deserialize to. + /// Stream to serialize. + /// Deserialized object from stream. + public T Deserialize(Stream requestStream) + { + try + { + byte[] utf8Json = null; + if (_debug) + { + var json = new StreamReader(requestStream).ReadToEnd(); + Console.WriteLine($"Lambda Deserialize {typeof(T).FullName}: {json}"); + utf8Json = UTF8Encoding.UTF8.GetBytes(json); + } + + if (utf8Json == null) + { + if (requestStream is MemoryStream ms) + { + utf8Json = ms.ToArray(); + } + else + { + using (var copy = new MemoryStream()) + { + requestStream.CopyTo(copy); + utf8Json = copy.ToArray(); + } + } + } + + return JsonSerializer.Deserialize(utf8Json, SerializerOptions); + } + catch (Exception e) + { + string message; + var targetType = typeof(T); + if (targetType == typeof(string)) + { + message = + $"Error converting the Lambda event JSON payload to a string. JSON strings must be quoted, for example \"Hello World\" in order to be converted to a string: {e.Message}"; + } + else + { + message = + $"Error converting the Lambda event JSON payload to type {targetType.FullName}: {e.Message}"; + } + + throw new JsonSerializerException(message, e); + } + } + } +} \ No newline at end of file diff --git a/Libraries/src/Amazon.Lambda.Serialization.SystemTextJson/LambdaJsonSerializer.cs b/Libraries/src/Amazon.Lambda.Serialization.SystemTextJson/LambdaJsonSerializer.cs index f5d585a63..6274b6d4c 100644 --- a/Libraries/src/Amazon.Lambda.Serialization.SystemTextJson/LambdaJsonSerializer.cs +++ b/Libraries/src/Amazon.Lambda.Serialization.SystemTextJson/LambdaJsonSerializer.cs @@ -16,7 +16,13 @@ namespace Amazon.Lambda.Serialization.SystemTextJson /// If the environment variable LAMBDA_NET_SERIALIZER_DEBUG is set to true the JSON coming /// in from Lambda and being sent back to Lambda will be logged. /// + /// + /// This serializer is obsolete because it uses inconsistent name casing when serializing to JSON. Fixing the + /// inconsistent casing issues would cause runtime breaking changes so the new type DefaultLambdaJsonSerializer was created. + /// https://github.com/aws/aws-lambda-dotnet/issues/624 + /// /// + [Obsolete("This serializer is obsolete because it uses inconsistent name casing when serializing to JSON. Lambda functions should use the DefaultLambdaJsonSerializer type.")] public class LambdaJsonSerializer : ILambdaSerializer { private const string DEBUG_ENVIRONMENT_VARIABLE_NAME = "LAMBDA_NET_SERIALIZER_DEBUG"; @@ -32,7 +38,7 @@ public LambdaJsonSerializer() { IgnoreNullValues = true, PropertyNameCaseInsensitive = true, - PropertyNamingPolicy = new AwsNamingPolicy() + PropertyNamingPolicy = new AwsNamingPolicy(JsonNamingPolicy.CamelCase) }; _options.Converters.Add(new DateTimeConverter()); diff --git a/Libraries/test/EventsTests.NETCore31/TestResponseCasing.cs b/Libraries/test/EventsTests.NETCore31/TestResponseCasing.cs new file mode 100644 index 000000000..f6385bc86 --- /dev/null +++ b/Libraries/test/EventsTests.NETCore31/TestResponseCasing.cs @@ -0,0 +1,55 @@ +using System.IO; + +using Xunit; + +using Amazon.Lambda.Serialization.SystemTextJson; +using Newtonsoft.Json.Linq; + +namespace EventsTests31 +{ + public class TestResponseCasing + { + [Fact] + public void TestPascalCase() + { + var serializer = new DefaultLambdaJsonSerializer(); + + var response = new DummyResponse + { + BingBong = "Joy" + }; + + MemoryStream ms = new MemoryStream(); + serializer.Serialize(response, ms); + ms.Position = 0; + var json = new StreamReader(ms).ReadToEnd(); + + var serialized = JObject.Parse(json); + Assert.Equal("Joy", serialized["BingBong"]?.ToString()); + } + + [Fact] + public void TestCamelCase() + { + var serializer = new CamelCaseLambdaJsonSerializer(); + + var response = new DummyResponse + { + BingBong = "Joy" + }; + + MemoryStream ms = new MemoryStream(); + serializer.Serialize(response, ms); + ms.Position = 0; + var json = new StreamReader(ms).ReadToEnd(); + + var serialized = JObject.Parse(json); + Assert.Equal("Joy", serialized["bingBong"]?.ToString()); + } + + public class DummyResponse + { + public string BingBong { get; set; } + } + } +} \ No newline at end of file diff --git a/Libraries/test/EventsTests.Shared/EventTests.cs b/Libraries/test/EventsTests.Shared/EventTests.cs index 9752e4d49..4cb328311 100644 --- a/Libraries/test/EventsTests.Shared/EventTests.cs +++ b/Libraries/test/EventsTests.Shared/EventTests.cs @@ -1,5 +1,6 @@ using Amazon.Lambda.Core; +#pragma warning disable 618 namespace Amazon.Lambda.Tests { using Amazon.Lambda; @@ -50,6 +51,7 @@ public MemoryStream LoadJsonTestFile(string filename) [InlineData(typeof(JsonSerializer))] #if NETCOREAPP_3_1 [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.LambdaJsonSerializer))] + [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] #endif public void HttpApiV2Format(Type serializerType) { @@ -131,6 +133,7 @@ public void SetHeadersToHttpApiV2Response() [InlineData(typeof(JsonSerializer))] #if NETCOREAPP_3_1 [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.LambdaJsonSerializer))] + [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] #endif public void S3PutTest(Type serializerType) { @@ -176,6 +179,7 @@ private void Handle(S3Event s3Event) [InlineData(typeof(JsonSerializer))] #if NETCOREAPP_3_1 [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.LambdaJsonSerializer))] + [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] #endif public void KinesisTest(Type serializerType) { @@ -220,6 +224,7 @@ private void Handle(KinesisEvent kinesisEvent) [InlineData(typeof(JsonSerializer))] #if NETCOREAPP_3_1 [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.LambdaJsonSerializer))] + [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] #endif public void DynamoDbUpdateTest(Type serializerType) { @@ -267,6 +272,7 @@ private static void Handle(DynamoDBEvent ddbEvent) [InlineData(typeof(JsonSerializer))] #if NETCOREAPP_3_1 [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.LambdaJsonSerializer))] + [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] #endif public void CognitoTest(Type serializerType) { @@ -307,6 +313,7 @@ private static void Handle(CognitoEvent cognitoEvent) [InlineData(typeof(JsonSerializer))] #if NETCOREAPP_3_1 [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.LambdaJsonSerializer))] + [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] #endif public void ConfigTest(Type serializerType) { @@ -340,6 +347,7 @@ private static void Handle(ConfigEvent configEvent) [InlineData(typeof(JsonSerializer))] #if NETCOREAPP_3_1 [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.LambdaJsonSerializer))] + [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] #endif public void SimpleEmailTest(Type serializerType) { @@ -406,6 +414,7 @@ public void SimpleEmailTest(Type serializerType) [InlineData(typeof(JsonSerializer))] #if NETCOREAPP_3_1 [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.LambdaJsonSerializer))] + [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] #endif public void SimpleEmailLambdaActionTest(Type serializerType) { @@ -428,6 +437,7 @@ public void SimpleEmailLambdaActionTest(Type serializerType) [InlineData(typeof(JsonSerializer))] #if NETCOREAPP_3_1 [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.LambdaJsonSerializer))] + [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] #endif public void SimpleEmailS3ActionTest(Type serializerType) { @@ -462,6 +472,7 @@ private static void Handle(SimpleEmailEvent sesE [InlineData(typeof(JsonSerializer))] #if NETCOREAPP_3_1 [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.LambdaJsonSerializer))] + [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] #endif public void SNSTest(Type serializerType) { @@ -509,6 +520,7 @@ private static void Handle(SNSEvent snsEvent) [InlineData(typeof(JsonSerializer))] #if NETCOREAPP_3_1 [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.LambdaJsonSerializer))] + [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] #endif public void SQSTest(Type serializerType) { @@ -577,6 +589,7 @@ private static void Handle(SQSEvent sqsEvent) [InlineData(typeof(JsonSerializer))] #if NETCOREAPP_3_1 [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.LambdaJsonSerializer))] + [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] #endif public void APIGatewayProxyRequestTest(Type serializerType) { @@ -651,6 +664,7 @@ private static APIGatewayProxyResponse Handle(APIGatewayProxyRequest apigProxyEv [InlineData(typeof(JsonSerializer))] #if NETCOREAPP_3_1 [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.LambdaJsonSerializer))] + [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] #endif public void APIGatewayProxyResponseTest(Type serializerType) { @@ -685,6 +699,7 @@ public void APIGatewayProxyResponseTest(Type serializerType) [InlineData(typeof(JsonSerializer))] #if NETCOREAPP_3_1 [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.LambdaJsonSerializer))] + [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] #endif public void APIGatewayAuthorizerResponseTest(Type serializerType) { @@ -739,6 +754,7 @@ public void APIGatewayAuthorizerResponseTest(Type serializerType) [InlineData(typeof(JsonSerializer))] #if NETCOREAPP_3_1 [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.LambdaJsonSerializer))] + [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] #endif public void ApplicationLoadBalancerRequestSingleValueTest(Type serializerType) { @@ -770,6 +786,7 @@ public void ApplicationLoadBalancerRequestSingleValueTest(Type serializerType) [InlineData(typeof(JsonSerializer))] #if NETCOREAPP_3_1 [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.LambdaJsonSerializer))] + [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] #endif public void ApplicationLoadBalancerRequestMultiValueTest(Type serializerType) { @@ -810,6 +827,7 @@ public void ApplicationLoadBalancerRequestMultiValueTest(Type serializerType) [InlineData(typeof(JsonSerializer))] #if NETCOREAPP_3_1 [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.LambdaJsonSerializer))] + [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] #endif public void ApplicationLoadBalancerSingleHeaderResponseTest(Type serializerType) { @@ -852,6 +870,7 @@ public void ApplicationLoadBalancerSingleHeaderResponseTest(Type serializerType) [InlineData(typeof(JsonSerializer))] #if NETCOREAPP_3_1 [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.LambdaJsonSerializer))] + [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] #endif public void ApplicationLoadBalancerMultiHeaderResponseTest(Type serializerType) { @@ -898,6 +917,7 @@ public void ApplicationLoadBalancerMultiHeaderResponseTest(Type serializerType) [InlineData(typeof(JsonSerializer))] #if NETCOREAPP_3_1 [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.LambdaJsonSerializer))] + [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] #endif public void LexEvent(Type serializerType) { @@ -939,6 +959,7 @@ public void LexEvent(Type serializerType) [InlineData(typeof(JsonSerializer))] #if NETCOREAPP_3_1 [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.LambdaJsonSerializer))] + [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] #endif public void LexResponse(Type serializerType) { @@ -987,6 +1008,7 @@ public void LexResponse(Type serializerType) [InlineData(typeof(JsonSerializer))] #if NETCOREAPP_3_1 [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.LambdaJsonSerializer))] + [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] #endif public void KinesisFirehoseEvent(Type serializerType) { @@ -1010,6 +1032,7 @@ public void KinesisFirehoseEvent(Type serializerType) [InlineData(typeof(JsonSerializer))] #if NETCOREAPP_3_1 [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.LambdaJsonSerializer))] + [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] #endif public void KinesisFirehoseResponseTest(Type serializerType) { @@ -1041,6 +1064,7 @@ public void KinesisFirehoseResponseTest(Type serializerType) [InlineData(typeof(JsonSerializer))] #if NETCOREAPP_3_1 [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.LambdaJsonSerializer))] + [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] #endif public void KinesisAnalyticsOutputDeliveryEvent(Type serializerType) { @@ -1060,6 +1084,7 @@ public void KinesisAnalyticsOutputDeliveryEvent(Type serializerType) [InlineData(typeof(JsonSerializer))] #if NETCOREAPP_3_1 [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.LambdaJsonSerializer))] + [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] #endif public void KinesisAnalyticsOutputDeliveryResponseTest(Type serializerType) { @@ -1087,6 +1112,7 @@ public void KinesisAnalyticsOutputDeliveryResponseTest(Type serializerType) [InlineData(typeof(JsonSerializer))] #if NETCOREAPP_3_1 [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.LambdaJsonSerializer))] + [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] #endif public void KinesisAnalyticsInputProcessingEventTest(Type serializerType) { @@ -1108,6 +1134,7 @@ public void KinesisAnalyticsInputProcessingEventTest(Type serializerType) [InlineData(typeof(JsonSerializer))] #if NETCOREAPP_3_1 [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.LambdaJsonSerializer))] + [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] #endif public void KinesisAnalyticsInputProcessingResponseTest(Type serializerType) { @@ -1139,6 +1166,7 @@ public void KinesisAnalyticsInputProcessingResponseTest(Type serializerType) [InlineData(typeof(JsonSerializer))] #if NETCOREAPP_3_1 [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.LambdaJsonSerializer))] + [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] #endif public void CloudWatchLogEvent(Type serializerType) { @@ -1168,6 +1196,7 @@ private string MemoryStreamToBase64String(MemoryStream ms) [InlineData(typeof(JsonSerializer))] #if NETCOREAPP_3_1 [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.LambdaJsonSerializer))] + [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] #endif public void BatchJobStateChangeEventTest(Type serializerType) { @@ -1219,6 +1248,7 @@ private void Handle(BatchJobStateChangeEvent jobStateChangeEvent) [InlineData(typeof(JsonSerializer))] #if NETCOREAPP_3_1 [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.LambdaJsonSerializer))] + [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] #endif public void ScheduledEventTest(Type serializerType) { @@ -1250,6 +1280,7 @@ private void Handle(ScheduledEvent scheduledEvent) [InlineData(typeof(JsonSerializer))] #if NETCOREAPP_3_1 [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.LambdaJsonSerializer))] + [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] #endif public void ECSContainerInstanceStateChangeEventTest(Type serializerType) { @@ -1300,6 +1331,7 @@ public void ECSContainerInstanceStateChangeEventTest(Type serializerType) [InlineData(typeof(JsonSerializer))] #if NETCOREAPP_3_1 [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.LambdaJsonSerializer))] + [InlineData(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] #endif public void ECSTaskStateChangeEventTest(Type serializerType) { @@ -1400,3 +1432,4 @@ class ClassUsingPascalCase } } } +#pragma warning restore 618 \ No newline at end of file