Skip to main content

System.Text.Json

Use System.Text.Json for JSON serialization of [JsonBlob] properties. This is the recommended serializer for new projects, especially those targeting AOT compilation.

Installation

dotnet add package Oproto.FluentDynamoDb.SystemTextJson

Configuration

Configure System.Text.Json at runtime using FluentDynamoDbOptions:

using Oproto.FluentDynamoDb;
using Oproto.FluentDynamoDb.SystemTextJson;

// Default options
var options = new FluentDynamoDbOptions()
.WithSystemTextJson();

var table = new DocumentTable(dynamoDbClient, "documents", options);

Custom JsonSerializerOptions

Pass custom JsonSerializerOptions to control serialization behavior:

var jsonOptions = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
WriteIndented = false,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
};

var options = new FluentDynamoDbOptions()
.WithSystemTextJson(jsonOptions);

var table = new DocumentTable(dynamoDbClient, "documents", options);

AOT-Compatible Configuration

For Native AOT scenarios, use a JsonSerializerContext to enable source-generated serialization:

// 1. Define your JSON context with types that need serialization
[JsonSerializable(typeof(DocumentContent))]
[JsonSerializable(typeof(List<string>))]
public partial class DocumentJsonContext : JsonSerializerContext
{
}

// 2. Configure FluentDynamoDbOptions with the context
var options = new FluentDynamoDbOptions()
.WithSystemTextJson(DocumentJsonContext.Default);

var table = new DocumentTable(dynamoDbClient, "documents", options);

Entity Definition

Use the [JsonBlob] attribute on properties that should be serialized as JSON strings in DynamoDB:

[DynamoDbTable("documents")]
public partial class Document
{
[PartitionKey]
[DynamoDbAttribute("pk")]
public string Pk { get; set; } = string.Empty;

[SortKey]
[DynamoDbAttribute("sk")]
public string Sk { get; set; } = string.Empty;

[DynamoDbAttribute("title")]
public string Title { get; set; } = string.Empty;

// This property will be serialized as a JSON string
[JsonBlob]
[DynamoDbAttribute("content")]
public DocumentContent Content { get; set; } = new();
}

public class DocumentContent
{
public string Body { get; set; } = string.Empty;
public List<string> Tags { get; set; } = new();
public Dictionary<string, object> Metadata { get; set; } = new();
}

Usage Example

// Configure options
var options = new FluentDynamoDbOptions()
.WithSystemTextJson();

var table = new DocumentTable(dynamoDbClient, "documents", options);

// Create a document with complex content
var document = new Document
{
Pk = Document.Keys.Pk("doc123"),
Sk = Document.Keys.Sk(),
Title = "My Document",
Content = new DocumentContent
{
Body = "Document body text",
Tags = new List<string> { "important", "draft" },
Metadata = new Dictionary<string, object>
{
{ "author", "John Doe" },
{ "version", 1 }
}
}
};

// Save - Content is automatically serialized to JSON
await table.Documents.PutAsync(document);

// Retrieve - Content is automatically deserialized from JSON
var retrieved = await table.Documents.GetAsync("doc123");
Console.WriteLine(retrieved?.Content.Body); // "Document body text"

Custom Converters

Register custom JsonConverter implementations for specialized serialization:

var jsonOptions = new JsonSerializerOptions();
jsonOptions.Converters.Add(new CustomDateTimeConverter());
jsonOptions.Converters.Add(new CustomEnumConverter());

var options = new FluentDynamoDbOptions()
.WithSystemTextJson(jsonOptions);

Example: Custom DateTime Converter

public class CustomDateTimeConverter : JsonConverter<DateTime>
{
private const string Format = "yyyy-MM-dd'T'HH:mm:ss.fffZ";

public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return DateTime.ParseExact(reader.GetString()!, Format, CultureInfo.InvariantCulture);
}

public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
{
writer.WriteStringValue(value.ToUniversalTime().ToString(Format, CultureInfo.InvariantCulture));
}
}

Error Handling

If you use [JsonBlob] without configuring a JSON serializer, you'll get a runtime exception:

// This will throw at runtime
var options = new FluentDynamoDbOptions(); // No JSON serializer configured!
var table = new DocumentTable(dynamoDbClient, "documents", options);

await table.Documents.PutAsync(document);
// InvalidOperationException: Property 'Content' has [JsonBlob] attribute but no JSON serializer is configured.
// Call .WithSystemTextJson() or .WithNewtonsoftJson() on FluentDynamoDbOptions.

Best Practices

  1. Use AOT configuration for production - Source-generated serialization is faster and supports Native AOT
  2. Configure once, reuse - Create FluentDynamoDbOptions once and reuse across table instances
  3. Match serialization settings - Use consistent JsonSerializerOptions across your application
  4. Handle nulls explicitly - Configure DefaultIgnoreCondition based on your requirements