JSON Support
JSON Storage
How JSON documents are stored, the XML mapping, and storage options
#JSON Storage
This guide covers how JSON documents are stored, the options that apply, and what is and is not available.
#Storing JSON Documents
#Basic Storage
JSON goes through the same PutDocumentAsync as XML:
var container = await db.OpenOrCreateContainerAsync("data");
// Store a JSON string (detected from the leading '{' or '[')
await container.PutDocumentAsync("doc.json", jsonString);
// Store from an object
var user = new { Name = "Alice", Age = 30 };
await container.PutDocumentAsync("user.json", JsonSerializer.Serialize(user));
// Force JSON handling regardless of content
await container.PutDocumentAsync("doc.json", jsonString,
new DocumentOptions { ContentType = ContentType.Json });
The document name's extension plays no part in detection.
#Storage Options
The options are the ordinary DocumentOptions:
|
Property |
Effect for JSON |
|---|---|
|
|
|
|
|
|
|
|
Metadata written with the document |
There are no JSON-specific storage options.
#What Is Stored
The JSON is converted to its fn:json-to-xml XML representation (namespace http://www.w3.org/2005/xpath-functions) and only that is stored:
await container.PutDocumentAsync("user.json", """
{
"name": "Alice",
"score": 95.5
}
""");
var doc = await container.GetDocumentAsync("user.json");
Console.WriteLine(doc!.ContentType); // Json
Console.WriteLine(await doc.GetContentAsync());
// <?xml version="1.0" encoding="utf-8"?><map xmlns="http://www.w3.org/2005/xpath-functions"><string key="name">Alice</string><number key="score">95.5</number></map>
// Back to JSON text
await foreach (var json in container.QueryAsync(
"xml-to-json(/fn:map)", variables: null, documentNameFilter: n => n == "user.json"))
Console.WriteLine(json); // {"name":"Alice","score":95.5}
The original text, including its whitespace, is not preserved. SizeBytes reports the UTF-8 length of the JSON as submitted. See JSON Support for the full mapping.
#JSON Validation
#Syntax
Content handled as JSON is parsed with System.Text.Json. Malformed JSON throws System.Text.Json.JsonException and nothing is written:
try
{
await container.PutDocumentAsync("bad.json", """{"id": 1,""");
}
catch (JsonException ex)
{
Console.WriteLine(ex.Message);
}
#Schema Validation
There is no JSON Schema validation. Validate in your application before calling PutDocumentAsync if you need it.
#Indexing JSON
Indexes are declared on ContainerOptions.Indexes when the container is created and apply to the XML representation. See JSON Indexing for what path patterns can and cannot address in JSON documents.
db.EnableIndexing();
var data = await db.CreateContainerAsync("data", opts =>
opts.Indexes.AddFullTextIndex()); // every element's text
#Bulk Import
There are no JSON import helpers. Store many documents with PutDocumentsAsync, which commits up to 1,000 documents per LMDB write transaction:
// One document per line of an NDJSON file
var inputs = File.ReadLines("data.ndjson")
.Where(line => line.Length > 0)
.Select((line, i) => new DocumentInput($"record-{i}.json", line));
int count = await container.PutDocumentsAsync(inputs);
Console.WriteLine($"Imported {count} documents");
#JSON Document Metadata
Metadata works exactly as for XML documents:
await container.PutDocumentAsync("user.json", json);
await container.SetMetadataAsync("user.json", "source", "api");
// Query by metadata (phx prefix is predeclared)
await foreach (var name in container.QueryAsync(
"/fn:map[phx:metadata(., 'source') = 'api']/fn:string[@key='name']/string()"))
{
Console.WriteLine(name);
}
#Migration from Other Formats
#From CSV
// Convert CSV rows to JSON documents
using var reader = new StreamReader("data.csv");
using var csv = new CsvReader(reader, CultureInfo.InvariantCulture);
int index = 0;
var inputs = new List<DocumentInput>();
foreach (var record in csv.GetRecords<dynamic>())
{
inputs.Add(new DocumentInput($"row-{index++}.json", JsonSerializer.Serialize(record)));
}
await container.PutDocumentsAsync(inputs);
(CsvReader is from the third-party CsvHelper library.)
#Best Practices
-
Consistent naming — Use a
.jsonextension so documents are recognisable in listings -
Index deliberately — Path patterns address element names, not JSON keys; see JSON Indexing
-
Validate before storing — There is no schema validation on write
-
Batch imports — Use
PutDocumentsAsyncfor large datasets -
Convert on the way out — Use
xml-to-json()when callers need JSON text
#Next Steps
|
Queries |
Indexing |
Performance |
|---|---|---|
|
JSON Queries Query patterns for JSON |
JSON Indexing Full indexing through XML |
Performance Tuning Optimization tips |