Summary
Enhance the JSON serialization and deserialization of typed configurations with JSON Schema support according to the JSON Schema 2020-12 specification. This enables schema validation of configuration JSON documents and improves interoperability with external tools that support JSON Schema.
Background
TopLogic's typed configuration system uses Java interfaces with annotations to describe configuration structures. While XML has been the primary serialization format, JSON support was added for scenarios requiring JSON data exchange. However, the existing JSON format lacked a formal schema definition that could be used for validation or documentation purposes.
Implementation
1. JSON Schema Model
A complete object model for JSON Schema 2020-12 has been implemented in com.top_logic.basic.json.schema.model using msgbuf-generated data classes:
- Schema base types: Schema, JsonSchema, JsonSchemaDocument
- Type schemas: ObjectSchema, ArraySchema, StringSchema, NumericSchema, BooleanSchema, NullSchema, EnumSchema, ConstSchema
- Composition schemas: AllOfSchema, AnyOfSchema, OneOfSchema, NotSchema, ConditionalSchema
- Reference schemas: RefSchema, DynamicRefSchema
- Boolean schemas: TrueSchema, FalseSchema
Serialization is handled by JsonSchemaWriter / JsonSchemaSerializer and parsing by JsonSchemaParser.
2. Schema Builder
The JsonConfigSchemaBuilder derives JSON schemas from ConfigurationDescriptor instances by:
- Mapping property types: Primitive types to appropriate JSON Schema types (string, number, integer, boolean)
- Handling nested configurations: ITEM properties become object schemas with type references
- Supporting polymorphism: Sub-types are combined using anyOf schemas
- Modeling inheritance: Super-type properties are composed using allOf schemas
- Translating constraints: Annotations like @Bound, @NonNegative, @Positive, @NonPositive, @Negative become minimum/`maximum` constraints
- Including metadata: I18N labels and descriptions from resources are added as title and description
- Handling enums: Enum constants are translated to enum schemas, respecting ExternallyNamed and ProtocolEnum, with documentation for each constant
- Handling maps: propertyNames constraints enforce key types for map properties, including enum and integer key types
- @Key annotation support: Properties annotated with @Key are modeled correctly, including polymorphic type hierarchies with keyed entries
- Root schema hoisting: The root schema is placed at the top level of the document with other type schemas in $defs
- External schema resolution: A SchemaResolver interface enables controlling how external/pre-defined schemas are referenced rather than inlined
- Custom type identifiers: A resolveTypeId() hook allows custom type identifier generation (e.g., for dynamic descriptors)
- @JsonBinding schema support: Properties with custom @JsonBinding annotations delegate schema generation to the binding's buildSchema() method
The schema uses a $type property to identify the concrete configuration or implementation type.
3. Schema-Aware Serialization
The JsonConfigurationWriter is enhanced with a schemaAware() mode that produces schema-conformant JSON:
- Type annotations are written as a $type property within the object (not as array wrapper)
- Implementation classes are referenced directly for PolymorphicConfiguration properties
- The format is designed for static validation against the generated schema
- A resolveTypeId() hook enables custom type identifiers in the serialized output
4. Schema-Aware Deserialization
The JsonConfigurationReader is enhanced with a corresponding schemaAware() mode:
- Reads the $type property to determine the concrete configuration type
- Supports both configuration interface types and implementation class references
- Maintains backwards compatibility with the stream-optimized format
5. @Final Annotation
A new @Final annotation for configuration interfaces marks types that have no sub-types. This allows the schema builder and serializer to omit the $type property when the configuration type is unambiguous, producing cleaner schemas and JSON. OpenAPI document interfaces were annotated as a first use case.
6. JsonBinding Framework
The @JsonBinding annotation (type and method level) associates configuration types with custom JsonValueBinding<T> implementations. Each binding provides:
- loadConfigItem() / saveConfigItem() for JSON serialization
- buildSchema() for JSON Schema generation
- ConfigurationValueCheck methods for validation
Binding implementations:
- ResKeyJsonBinding - Resource keys serialized as JSON objects with language-keyed fields; literal ResKeys supported
- StringArrayJsonBinding - String arrays
- BinaryDataJsonBinding - Binary data serialized as base64-encoded objects with name and contentType metadata
- HtmlResKeyJsonBinding - HTML resource keys
- StructuredTextJsonBinding - Structured text values
- I18NStructuredTextJsonBinding - Internationalized structured text values
7. Descriptor Enhancement
ConfigurationDescriptor is extended with:
- getInstanceType(): Returns the implementation type A` if the configuration is a `PolymorphicConfiguration<A>, enabling proper schema generation for configured instances.
Schema Structure
For a configuration type A` with sub-types `B1, B2:
value-schema(A) := anyOf( allOf(required-type-ref(A), properties(A)), // if A is non-abstract ref:value-schema(B1), ref:value-schema(B2) ) properties(A) := allOf( local-properties(A), ref:properties(SuperType1), ... )
The root schema is hoisted to the top level of the JsonSchemaDocument, with all other type schemas placed in $defs and referenced via $ref.
Testing
- TestJsonConfigurationWithSchema - Comprehensive round-trip tests for schema-aware serialization: polymorphic base values, abstract types with multiple sub-types, configured implementations, and inheritance hierarchies
- TestJsonConfigSchemaBuilderEnums - Enum handling including ExternallyNamed and ProtocolEnum
- TestJsonConfigSchemaBuilderKeyAnnotation - @Key annotation support with polymorphic types and enum/integer map keys
- TestJsonSchemaParser / TestJsonSchemaSerializer - Unit tests for schema model parsing and serialization
- Schema-aware vs. non-schema-aware serialization comparison tests
- OpenAPI JSON binding round-trip tests
- Tests for all JsonBinding implementations (BinaryData, HtmlResKey, StructuredText, I18NStructuredText)
- Round-trip serialization with schema validation using the networknt/json-schema-validator library (test scope)
Use Cases
- External Tool Integration: Generated schemas can be used by IDE plugins, API documentation tools, or configuration editors
- Validation: Configuration JSON can be validated before processing
- Documentation: Schemas serve as machine-readable API documentation with I18N labels and descriptions
- Code Generation: External tools can generate client code from schemas