Skip to main content

Newtonsoft.Json

Use Newtonsoft.Json (Json.NET) for JSON serialization of [JsonBlob] properties. This serializer is useful when you need advanced features like polymorphic serialization or have existing Newtonsoft.Json infrastructure.

Installation

dotnet add package Oproto.FluentDynamoDb.NewtonsoftJson

Configuration

Configure Newtonsoft.Json at runtime using FluentDynamoDbOptions:

using Oproto.FluentDynamoDb;
using Oproto.FluentDynamoDb.NewtonsoftJson;

// Default settings
var options = new FluentDynamoDbOptions()
.WithNewtonsoftJson();

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

Custom JsonSerializerSettings

Pass custom JsonSerializerSettings to control serialization behavior:

using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;

var jsonSettings = new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
NullValueHandling = NullValueHandling.Ignore,
DateFormatHandling = DateFormatHandling.IsoDateFormat,
Formatting = Formatting.None
};

var options = new FluentDynamoDbOptions()
.WithNewtonsoftJson(jsonSettings);

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()
.WithNewtonsoftJson();

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 jsonSettings = new JsonSerializerSettings();
jsonSettings.Converters.Add(new CustomDateTimeConverter());
jsonSettings.Converters.Add(new StringEnumConverter());

var options = new FluentDynamoDbOptions()
.WithNewtonsoftJson(jsonSettings);

Example: Polymorphic Serialization

Newtonsoft.Json excels at polymorphic serialization with type handling:

var jsonSettings = new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto,
SerializationBinder = new KnownTypesBinder
{
KnownTypes = new List<Type> { typeof(TextContent), typeof(ImageContent) }
}
};

var options = new FluentDynamoDbOptions()
.WithNewtonsoftJson(jsonSettings);
// Base class
public abstract class ContentBase
{
public string Type { get; set; } = string.Empty;
}

// Derived types
public class TextContent : ContentBase
{
public string Text { get; set; } = string.Empty;
}

public class ImageContent : ContentBase
{
public string Url { get; set; } = string.Empty;
public int Width { get; set; }
public int Height { get; set; }
}

// Entity with polymorphic content
[DynamoDbTable("posts")]
public partial class Post
{
[PartitionKey]
[DynamoDbAttribute("pk")]
public string Pk { get; set; } = string.Empty;

[JsonBlob]
[DynamoDbAttribute("content")]
public ContentBase Content { get; set; } = null!;
}

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.

When to Use Newtonsoft.Json

Consider Newtonsoft.Json when you need:

  • Polymorphic serialization with TypeNameHandling
  • Custom contract resolvers for complex mapping scenarios
  • Reference handling for circular references
  • Compatibility with existing Newtonsoft.Json infrastructure

For new projects without these requirements, consider using System.Text.Json for better performance and AOT support.

Best Practices

  1. Configure once, reuse - Create FluentDynamoDbOptions once and reuse across table instances
  2. Match serialization settings - Use consistent JsonSerializerSettings across your application
  3. Avoid TypeNameHandling.All - Use TypeNameHandling.Auto or TypeNameHandling.Objects with a custom binder for security
  4. Handle nulls explicitly - Configure NullValueHandling based on your requirements