using Amazon.Lambda.Serialization.SystemTextJson.Converters; using System; using System.Collections.Generic; using System.IO; using System.Text; using System.Text.Json; using System.Text.Json.Serialization; namespace Amazon.Lambda.Serialization.SystemTextJson { /// /// Base class of serializers using System.Text.Json /// public abstract class AbstractLambdaJsonSerializer { private const string DEBUG_ENVIRONMENT_VARIABLE_NAME = "LAMBDA_NET_SERIALIZER_DEBUG"; private readonly bool _debug; /// /// Options settings used for the JSON writer /// protected JsonWriterOptions WriterOptions { get; } /// /// Create instance /// /// protected AbstractLambdaJsonSerializer(Action jsonWriterCustomizer) { WriterOptions = new JsonWriterOptions() { Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping }; jsonWriterCustomizer?.Invoke(this.WriterOptions); this._debug = string.Equals(Environment.GetEnvironmentVariable(DEBUG_ENVIRONMENT_VARIABLE_NAME), "true", StringComparison.OrdinalIgnoreCase); } /// /// 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 debugStream = new MemoryStream()) using (var utf8Writer = new Utf8JsonWriter(debugStream, WriterOptions)) { InternalSerialize(utf8Writer, response); debugStream.Position = 0; using var debugReader = new StreamReader(debugStream); var jsonDocument = debugReader.ReadToEnd(); 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)) { InternalSerialize(writer, response); } } } 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 InternalDeserialize(utf8Json); } catch (Exception e) { throw new JsonSerializerException($"Error converting the Lambda event JSON payload to type {typeof(T).FullName}: {e.Message}", e); } } /// /// Create the default instance of JsonSerializerOptions used in serializer /// /// protected virtual JsonSerializerOptions CreateDefaultJsonSerializationOptions() { var serializer = new JsonSerializerOptions() { #if NET6_0_OR_GREATER DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, #else IgnoreNullValues = true, #endif PropertyNameCaseInsensitive = true, PropertyNamingPolicy = new AwsNamingPolicy(), Converters = { new DateTimeConverter(), new MemoryStreamConverter(), new ConstantClassConverter(), new ByteArrayConverter() } }; return serializer; } /// /// Perform the actual serialization after the public method had done the safety checks. /// /// /// /// protected abstract void InternalSerialize(Utf8JsonWriter writer, T response); /// /// Perform the actual deserialization after the public method had done the safety checks. /// /// /// /// protected abstract T InternalDeserialize(byte[] utf8Json); } }