Generated JSON mappers
Apply [RespireJson] to a partial class or record class to generate ModelNameJsonMapper: JSON
codecs, a key template, and typed RedisJSON operations, with no runtime reflection. Install
Respire.Json; its Respire dependency supplies the source generator. Supports .NET 8 and .NET 10,
including Native AOT.
using Respire;
using Respire.Json;
var user = new User("42", "Ada", null);
RespireKey key = UserJsonMapper.GetKey(user); // user:{42}
byte[] json = UserJsonMapper.ToJson(user);
User? copy = UserJsonMapper.FromJson(json);
[RespireJson("user:{{{Id}}}")]
public partial record User(string Id, string? Name, int? Age);
The mapper lives in the model's namespace. Its generated JsonTypeInfo property works with
JsonSerializer and every metadata overload on RespireJsonClient. No JsonSerializerContext is
needed. The generated options use an empty resolver, so unsupported types cannot fall back to
reflection. Serialization options and custom converters are not configurable.
RedisJSON requirements and operations
Redis I/O needs JSON.GET, JSON.SET, and JSONPath ($), as in Redis 8 or Redis Stack with
RedisJSON 2 or later. Without the module, Redis returns an unknown-command error. The local codec
methods do not need Redis.
Pass a RespireJsonClient, for example client.Json (see Redis JSON):
| Call | Effect |
|---|---|
UserJsonMapper.SetAsync(jsonClient, user) | Writes the document at the template key |
UserJsonMapper.SetAsync(jsonClient, key, user) | Writes at an explicit key; a null model stores a JSON null document |
UserJsonMapper.GetAsync(jsonClient, key) | Returns a RespireJsonGetResult<User> |
UserJsonMapper.SetNameAsync(jsonClient, key, "Grace") | Writes one property |
UserJsonMapper.GetNameAsync(jsonClient, key) | Reads one property |
UserJsonMapper.SetAgeAsync(jsonClient, key, null) | Stores JSON null in a nullable property |
- Both
SetAsyncoverloads take aRespireJsonSetCondition(None,Nx,Xx) and return false when a conditional write is rejected. - The client's key prefix applies to all operations. An explicit key need not match the template.
- Every property gets
{Property}Path,Get{Property}Async, andSet{Property}Async, for exampleNamePath. They work on one top-level property. For nested or arbitrary paths and wildcards, useRespireJsonClientwith explicit metadata. - Paths use JSONPath bracket notation and escape quotes and backslashes in storage names. Unicode
and control characters stay literal, as RedisJSON requires (no
\uor\nescapes). Dots and apostrophes in a[JsonPropertyName]name are literal too.
Results and errors:
- A missing key or match returns
Found = false. - Stored JSON null returns
Found = trueand the default value: null for nullable properties and reference models, the scalar default (such as zero) for non-nullable value types. - A read with multiple matches throws
InvalidOperationException. Malformed or incompatible JSON throwsJsonException. - Property writes can create a missing property in an existing object, but not a missing document.
Module path/type errors throw
RespireServerException. - Every I/O method takes a
CancellationToken. Cancellation after submission does not guarantee Redis did not apply a write.
Supported shapes and JSON rules
Models:
- Public or internal, top-level, non-generic, non-abstract partial classes or record classes without inheritance.
- Public instance properties need public get and set/init accessors. Private state is not serialized.
- Types match generated hash codecs:
string,bool,int,long,double,decimal,Guid, andDateTimeOffset, including nullable variants. - Positional records and accessible constructors whose parameter names and types match properties are supported.
JSON rules:
- Property names are case-sensitive.
[JsonPropertyName]changes the storage name and path; key placeholders still use the C# name. - All properties are written, including nulls.
- Missing nullable properties read as null. Missing or null non-nullable properties throw
JsonException. The C#requiredmodifier affects construction only. - Unknown members are ignored; duplicate members use the last value.
FromJsonaccepts a null document. - Numbers use JSON number syntax and invariant culture; numbers in strings are rejected.
Non-finite doubles cannot be written.
GuidandDateTimeOffsetuse System.Text.Json's formats. - Invalid, truncated, out-of-range, or trailing JSON fails decoding.
- Codecs allocate model objects and byte arrays.
Key templates follow the hash mapper rules: {Id} inserts a non-nullable scalar property, {{ and
}} insert literal braces, and numbers use invariant formatting. A null model or null required
string key property fails before I/O. Nullable placeholders and malformed templates are rejected.
RESP005 reports unsupported shapes, inaccessible construction, invalid templates, mapper name
collisions, duplicate storage names, and unsupported attributes. Invalid models emit no mapper.
Unsupported: collections (except indexed float[]/double[] vectors in
generated Search schemas), nested objects, public fields, indexers,
custom converters, [JsonIgnore], [JsonInclude], extension data, and polymorphic/type attributes.
For hash storage with field TTL and change tracking, see generated hash codecs;
for indexes, see generated Search schemas. The
Native AOT conformance sample runs
generated hash, JSON, and Search operations on net8.0 and net10.0 with reflection-based JSON
serialization disabled.