Libraries/src/Amazon.Lambda.Serialization.SystemTextJson/SourceGeneratorLambdaJsonSerializer.cs (70 lines of code) (raw):
#if NET6_0_OR_GREATER
using System;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.Json.Serialization.Metadata;
using Amazon.Lambda.Core;
using Amazon.Lambda.Serialization.SystemTextJson.Converters;
namespace Amazon.Lambda.Serialization.SystemTextJson
{
/// <summary>
/// ILambdaSerializer implementation that supports the source generator support of System.Text.Json. To use this serializer define
/// a partial JsonSerializerContext class with attributes for the types to be serialized.
///
/// [JsonSerializable(typeof(APIGatewayHttpApiV2ProxyRequest))]
/// [JsonSerializable(typeof(APIGatewayHttpApiV2ProxyResponse))]
/// public partial class APIGatewaySerializerContext : JsonSerializerContext
/// {
/// }
///
/// Register the serializer with the LambdaSerializer attribute specifying the defined JsonSerializerContext
///
/// [assembly: LambdaSerializer(typeof(SourceGeneratorLambdaJsonSerializer<APIGatewayExampleImage.MyJsonContext>))]
///
/// When the class is compiled it will generate all of the JSON serialization code to convert between JSON and the list types. This
/// will avoid any reflection based serialization.
/// </summary>
/// <typeparam name="TSGContext"></typeparam>
public class SourceGeneratorLambdaJsonSerializer<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TSGContext> : AbstractLambdaJsonSerializer, ILambdaSerializer where TSGContext : JsonSerializerContext
{
TSGContext _jsonSerializerContext;
/// <summary>
/// The options used to serialize JSON object.
/// </summary>
protected JsonSerializerOptions SerializerOptions { get; }
/// <summary>
/// Constructs instance of serializer.
/// </summary>
public SourceGeneratorLambdaJsonSerializer()
: this(null, null)
{
}
/// <summary>
/// Constructs instance of serializer with the option to customize the JsonSerializerOptions after the
/// Amazon.Lambda.Serialization.SystemTextJson's default settings have been applied.
/// </summary>
/// <param name="customizer"></param>
public SourceGeneratorLambdaJsonSerializer(Action<JsonSerializerOptions> customizer)
: this(customizer, null)
{
}
/// <summary>
/// Constructs instance of serializer with the option to customize the JsonWriterOptions after the
/// Amazon.Lambda.Serialization.SystemTextJson's default settings have been applied.
/// </summary>
/// <param name="jsonWriterCustomizer"></param>
public SourceGeneratorLambdaJsonSerializer(Action<JsonWriterOptions> jsonWriterCustomizer)
: this(null, jsonWriterCustomizer)
{
}
/// <summary>
/// Constructs instance of serializer with the option to customize the JsonWriterOptions after the
/// Amazon.Lambda.Serialization.SystemTextJson's default settings have been applied.
/// </summary>
/// <param name="customizer"></param>
/// <param name="jsonWriterCustomizer"></param>
public SourceGeneratorLambdaJsonSerializer(Action<JsonSerializerOptions> customizer, Action<JsonWriterOptions> jsonWriterCustomizer)
: base(jsonWriterCustomizer)
{
SerializerOptions = CreateDefaultJsonSerializationOptions();
customizer?.Invoke(this.SerializerOptions);
var constructor = typeof(TSGContext).GetConstructor(new Type[] { typeof(JsonSerializerOptions) });
if(constructor == null)
{
throw new ApplicationException($"The serializer {typeof(TSGContext).FullName} is missing a constructor that takes in JsonSerializerOptions object");
}
_jsonSerializerContext = constructor.Invoke(new object[] { this.SerializerOptions }) as TSGContext;
}
/// <summary>
/// Constructs instance of serializer with the option to customize the JsonWriterOptions after the
/// Amazon.Lambda.Serialization.SystemTextJson's default settings have been applied.
/// </summary>
/// <param name="jsonSerializerContext"></param>
/// <param name="customizer"></param>
/// <param name="jsonWriterCustomizer"></param>
[Obsolete("The method is marked obsolete because the jsonSerializerContext parameter is unlikely to be created with the required JsonSerializerOptions for Lambda serialization. " +
"This will trigger confusing NullReferenceException. The constructors that don't take an instance of jsonSerializerContext can be used for Native AOT because the reflection " +
"calls have been annotated to make them Native AOT compatible.")]
public SourceGeneratorLambdaJsonSerializer(TSGContext jsonSerializerContext, Action<JsonSerializerOptions> customizer = null, Action<JsonWriterOptions> jsonWriterCustomizer = null)
: base(jsonWriterCustomizer)
{
SerializerOptions = CreateDefaultJsonSerializationOptions();
customizer?.Invoke(this.SerializerOptions);
_jsonSerializerContext = jsonSerializerContext;
}
/// <inheritdoc/>
protected override void InternalSerialize<T>(Utf8JsonWriter writer, T response)
{
var jsonTypeInfo = _jsonSerializerContext.GetTypeInfo(typeof(T)) as JsonTypeInfo<T>;
if (jsonTypeInfo == null)
{
throw new JsonSerializerException($"No JsonTypeInfo registered in {_jsonSerializerContext.GetType().FullName} for type {typeof(T).FullName}.");
}
JsonSerializer.Serialize(writer, response, jsonTypeInfo);
}
/// <inheritdoc/>
protected override T InternalDeserialize<T>(byte[] utf8Json)
{
var jsonTypeInfo = _jsonSerializerContext.GetTypeInfo(typeof(T)) as JsonTypeInfo<T>;
if (jsonTypeInfo == null)
{
throw new JsonSerializerException($"No JsonTypeInfo registered in {_jsonSerializerContext.GetType().FullName} for type {typeof(T).FullName}.");
}
return JsonSerializer.Deserialize<T>(utf8Json, jsonTypeInfo);
}
}
}
#endif