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. /// /// /// 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"; private readonly JsonSerializerOptions _options; private readonly JsonWriterOptions WriterOptions; private readonly bool _debug; /// /// Constructs instance of serializer. /// public LambdaJsonSerializer() { _options = new JsonSerializerOptions() { IgnoreNullValues = true, PropertyNameCaseInsensitive = true, PropertyNamingPolicy = new AwsNamingPolicy(JsonNamingPolicy.CamelCase) }; _options.Converters.Add(new DateTimeConverter()); _options.Converters.Add(new MemoryStreamConverter()); _options.Converters.Add(new ConstantClassConverter()); _options.Converters.Add(new ByteArrayConverter()); WriterOptions = new JsonWriterOptions() { Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping }; if (string.Equals(Environment.GetEnvironmentVariable(DEBUG_ENVIRONMENT_VARIABLE_NAME), "true", StringComparison.OrdinalIgnoreCase)) { this._debug = true; } } /// /// Constructs instance of serializer with the option to customize the JsonSerializerOptions after the /// Amazon.Lambda.Serialization.SystemTextJson's default settings have been applied. /// /// public LambdaJsonSerializer(Action customizer) : this() { customizer?.Invoke(this._options); } /// /// Constructs instance of serializer with the option to customize the JsonSerializerOptions after the /// Amazon.Lambda.Serialization.SystemTextJson's default settings have been applied. /// /// /// public LambdaJsonSerializer(Action customizer, Action jsonWriterCustomizer) : this(customizer) { jsonWriterCustomizer?.Invoke(this.WriterOptions); } /// /// 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, WriterOptions)) { JsonSerializer.Serialize(utf8Writer, response); 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, WriterOptions)) { JsonSerializer.Serialize(writer, response, _options); } } } 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, _options); } 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); } } } }