JSON Converter & Registry
1. Requirements Document
1.1 Overview
This library provides a compile-time generated JSON conversion registry that enables applications to convert raw JSON data into strongly typed objects using a centralized mechanism.
Instead of relying on conditional logic, reflection, or scattered factory calls, the solution introduces:
- Explicit opt-in via annotations
- Build-time discovery of eligible models
- A generated registry mapping types to JSON factory functions
- A single runtime entry point for decoding objects and lists
The design philosophy mirrors established dependency-injection patterns where registration is code-generated and initialization is explicitly invoked by the developer.
1.2 User Stories
- US-1: As an application developer, I want to convert JSON into typed objects without writing repetitive parsing logic.
- US-2: As a framework user, I want support for multiple model-generation strategies without changing runtime code.
- US-3: As a plugin author, I want to decode models dynamically using their type.
- US-4: As a maintainer, I want all supported models to be discoverable at compile time.
- US-5: As a performance-conscious developer, I want zero reflection and predictable runtime behavior.
1.3 Functional Requirements
- Annotation-Driven: Developers must be able to mark models as eligible for JSON conversion using a package-specific annotation.
- Single Strategy: Exactly one conversion strategy must be declared per model.
- Registry Generation: The build system must scan annotated models and generate a registry file.
- Type Mapping: The generated registry must map runtime types to JSON factory functions.
- Initialization: The library must provide a standard initialization hook for registering generated factories.
- Runtime API: The runtime API must support:
- Decoding a single object from a map
- Decoding a list of objects from an iterable
- Type-safe generic decoding
- Result-Based Results: The system must provide structured success and failure results.
1.4 Non-Functional Requirements
- No runtime reflection
- Compatible with Flutter and pure Dart environments
- Deterministic build output
- Minimal runtime overhead
- Clear error messages for misconfiguration
- Safe handling of invalid or unexpected JSON shapes
1.5 Constraints & Assumptions
- Only explicitly annotated models participate in conversion
- Code generation is mandatory
- Initialization must be performed exactly once
- JSON input is assumed to be decoded into map / list structures prior to usage
2. Technical Document
2.1 Architecture
System Architecture
The workflow is split into two phases: specific factory discovery at Build Time and generic type resolution at Runtime. The Generated Registry acts as the bridge, ensuring no reflection is required.
2.2 API Design
Annotation & Strategy Declaration
class KConvert {
final KConvertType type;
const KConvert({required this.type});
}
enum KConvertType { jsonSerializable, freezed, custom }@JsonSerializable()
@KConvert(type: KConvertType.jsonSerializable)
class MyClass {
// ...
}Generated Registry (Example Output)
// Generated Code - Do not modify
typedef JsonFactory<T> = T Function(Map<String, dynamic> json);
const Map<Type, JsonFactory> factories = {
StorageBox: StorageBox.fromJson,
AppConfig: AppConfig.fromJson,
};Initialization Contract
@KConvertInit
void registerConverters() async =>
await KConverter.instance.register(factories);Runtime Decoding API
class JsonTypeParser {
static KResult<T> decodeMap<T>(Map<String, dynamic> values) {
// generic lookup logic
}
static KResult<List<T>> decodeList<T>(Iterable values) {
// generic list lookup logic
}
}2.3 Failure Model
The runtime decoding API follows a result-based error model rather than throwing unchecked exceptions. All decode operations return a KResult<T> wrapper.
Failure Categories:
RegistryNotInitialized: Init hook was never called.TypeNotRegistered: RequestedTis not in the map.InvalidStructure: JSON shape doesn't match expectations.FactoryFailure: ThefromJsonmethod itself threw an error.