Skip to content

Configuration Resolution

← Architecture


This chapter defines how configuration is resolved across all codec features. Understanding this hierarchy is essential as it applies to every serialization target (type, ID, reference, supertype) and feature.

1. Two-Dimensional Configuration Model

Configuration resolution operates across two orthogonal dimensions:

  1. Source Hierarchy (Vertical): Where configuration comes from (runtime vs. static)
  2. Scope Chain (Horizontal): What EMF element configuration applies to (feature vs. class vs. global)

This model allows fine-grained control: you can set a global default in the model, override it per-class at runtime, and further override it per-feature for specific operations.


2. Dimension 1: Source Hierarchy (Vertical)

Configuration can be provided at different sources. Higher priority sources override lower ones.

PrioritySourceLifecycleConfiguration Methods
1 (highest)Load/Save OptionsPer-operationProperty map
2ResourcePer-resourceProperty map, ConfigBuilder
3ResourceFactoryPer-factoryProperty map, ConfigBuilder
4Jackson Module ConfigPer-codecProperty map, ConfigBuilder
5EAnnotationsPer-model (static)Ecore model annotations
6 (lowest)Built-in DefaultsGlobalHardcoded in codec

Key principle: Dynamic overrides static. Runtime settings always win over model-defined settings.

Load/Save Options    ← dynamic, per-operation

Resource config      ← can be set when creating resource

ResourceFactory      ← factory-wide defaults

Jackson Module       ← codec-wide defaults

EAnnotations         ← static, defined in .ecore model

Built-in Defaults    ← hardcoded fallback

    ════════════════════════
    │ Effective Value      │
    ════════════════════════

3. Dimension 2: Scope Chain (Horizontal)

Within any single source level, configuration can be scoped to different EMF elements. More specific scopes override less specific ones.

PriorityScopeApplies To
1 (highest)EReference/EAttributeSingle feature
2EClassAll instances of that class
3EPackageAll classes in that package
4GlobalAll objects in codec (with optional StrategyScope)
5 (lowest)DefaultBuilt-in fallback
EReference annotation    ← most specific

EClass annotation        ← class-level default

EPackage annotation      ← package-wide default

Global config            ← codec-wide (respects StrategyScope)

Built-in Default         ← hardcoded fallback

    ════════════════════════
    │ Effective Value      │
    ════════════════════════

3.1 The EPackage Scope Is Annotation-Internal

The EPackage scope exists only within the annotation source. It is merged into each class's annotation configuration when the package profile is built, so the vertical source hierarchy of §2 keeps seeing exactly one annotation layer — options, resource, factory and module are unaffected by its existence.

Two consequences follow from that, and both are deliberate:

  • Merging is per property. A class that configures one key inherits the package's remaining keys rather than resetting them to built-in defaults. A class states an exception to its package.
  • The layering is driven by which keys an annotation actually contains, not by comparing values against defaults. A class explicitly restating a default value still overrides its package — the two are different statements, and only the annotation's own key set can tell them apart.

Resolution uses the directly owning package only. Subpackage hierarchies are not traversed: the codec resolves through EClass.getEPackage() everywhere and never walks getESubpackages() / getESuperPackage().

Scopes do not participate (typeScope, typeFormatScope, idScope, idFormatScope). A scope says where in a document a setting applies, which is a property of the operation, not of a model — see 16-annotation-reference.md and 99-open-questions.md Q1.


4. Combined Resolution Algorithm

When resolving a configuration value, both dimensions are considered:

For property P on EReference R of EClass C:

1. Check Load/Save options for R.P, then C.P, then global P
2. Check Resource config for R.P, then C.P, then global P
3. Check ResourceFactory for R.P, then C.P, then global P
4. Check Jackson Module for R.P, then C.P, then global P
5. Check EAnnotation on R for P
6. Check EAnnotation on C for P
7. Check global config for P (respecting StrategyScope)
8. Use built-in default for P

First non-null value wins.

Example Resolution:

For typeStrategy on Person.address (an EReference):

  1. Load options: Person.address.typeStrategy → not set
  2. Load options: Person.typeStrategy → not set
  3. Load options: typeStrategy → not set
  4. Resource config: same pattern → not set
  5. ResourceFactory: same pattern → not set
  6. Jackson Module: same pattern → not set
  7. EAnnotation on address feature: @CODEC(codec.type="...") → not set
  8. EAnnotation on Person class: @CODEC(codec.typeStrategy="NAME")found: NAME

Result: typeStrategy = NAME

Resolution identity — instance, not name. Class- and feature-level configuration — from every source, annotations included — is matched against the concrete EClass / EStructuralFeature instance (object identity; EMF does not override equals/hashCode). It is never keyed on the bare class name or a type URI. Two same-named EClasses from two package versions sharing one nsURI are distinct instances and resolve to their own configuration. The instance reaches the resolver from the object being (de)serialized (eObject.eClass()) or from a root-type hint held as an EClass.


5. StrategyScope (Global Level Modifier)

At the Global level (codec-wide configuration), certain properties support StrategyScope to control where they apply. This provides a middle ground between "everywhere" and "nowhere."

StrategyScopeApplies To
ALLRoot + all containments + all non-containments (default)
ROOT_ONLYRoot object only
ROOT_CONTAINMENTRoot + objects accessed via containment references
ROOT_NON_CONTAINMENTRoot + objects accessed via non-containment references

Example: Global typeStrategy=NAME with typeScope=ROOT_ONLY:

  • Root object uses NAME strategy
  • All nested objects fall back to default (URI)

Configuration:

java
CodecConfiguration.builder()
    .typeStrategy(TypeStrategy.NAME)
    .typeScope(StrategyScope.ROOT_ONLY)  // Only applies to root
    .build();

EAnnotation equivalent:

xml
<eAnnotations source="http://eclipse.org/fennec/codec">
  <details key="codec.typeStrategy" value="NAME"/>
  <details key="codec.typeScope" value="ROOT_ONLY"/>
</eAnnotations>

6. Configuration Dependencies

Certain settings have logical dependencies. When a parent setting is disabled, dependent settings are implicitly disabled.

Type and SuperType Dependency

serializeTypeserializeSuperTypes (configured)serializeSuperTypes (effective)
truetruetrue
truefalsefalse
falsetruefalse (implicitly disabled)
falsefalsefalse

Rationale: Supertypes are type information. If type serialization is disabled, serializing supertypes would be inconsistent.

Smart Compression Dependencies

Smart compression affects multiple targets:

  • Type serialization (omit when inferable)
  • Reference type (omit when equals declared type)
  • Same-schema names (use simple name instead of URI)

7. EAnnotation and Config Builder Parity

Every codec feature that can be configured via EAnnotations can also be configured via Config Builder, and vice versa.

EAnnotation (static, in model):

xml
<eStructuralFeatures name="firstName">
  <eAnnotations source="http://eclipse.org/fennec/codec">
    <details key="key" value="first_name"/>
  </eAnnotations>
</eStructuralFeatures>

Config Builder (dynamic, at runtime):

java
FeatureSerializationConfig.builder()
    .feature("firstName")
    .key("first_name")
    .build();

Load/Save options (per-operation override):

java
Map<String, Object> options = new HashMap<>();
options.put("Person.firstName.key", "givenName");  // Overrides both above
resource.save(outputStream, options);

8. EffectiveConfig Pattern

The codec resolves configuration per conversion step, lazily and keyed by concrete EMF instance. It does not build a single process-global name→config map.

java
// Illustrative only — not a literal API. In code, ConfigurationResolver merges
// lazily per instance and EffectiveCodecConfig is a thin delegator over it.
EffectiveCodecConfig effectiveConfig = /* built from all levels for this operation */;

// Per-class / per-feature configs, cached by INSTANCE:
ClassConfig   classConfig   = effectiveConfig.resolveClassConfig(personClass);
FeatureConfig featureConfig = effectiveConfig.resolveFeatureConfig(firstNameFeature);

Instance identity, not name. The per-class and per-feature caches are keyed by the EClass / EStructuralFeature instance (computeIfAbsent), never by class name or type URI. The annotation layer is sourced from MetadataService.getClassMetadata(EClass) / getClassProfile(EClass, "codec") — the fingerprinted PackageMetadata is the entry point (see fingerprinting workdoc, F5) — and the derived config is memoized in that same instance cache, so the MetadataService is not re-queried on every call. No bare-name map sits between the metadata and the resolved config; two same-named EClasses from two package versions of one nsURI are distinct instances and resolve to their own config.

This ensures:

  • Configuration for a given instance is resolved once per operation, then cached — not recomputed on every serialization call.
  • The resolved view is immutable per selected package version and is a per-load derived view, not a process-global snapshot.
  • Lazy, instance-keyed caching optimizes memory for large models and is naturally multi-version-correct.

Version selection / pinning — forward reference, [not yet implemented]. Which package version an nsURI resolves to (when several coexist under one nsURI) is decided by the per-load pin — see the fingerprinting workdoc §2b [DECIDED]. A.1 delivers the instance-identity part above (the caller already holds the concrete instance); the pin/selection layer lands in Phase A.3 / Phase B.

Behavior change — multi-version correctness [A.1]. Because config matches by instance, an EClass that is not registered in the MetadataService no longer silently inherits the config of a same-named, registered class. Previously the name-keyed bridge applied a registered class's config to any same-named class. This is the one intended deviation from prior observable behavior; everything else is behavior-stable.


9. ResourceFactory Configuration

The CodecResourceFactory provides Level 2 configuration in the hierarchy. It supports both constructor-based and setter-based configuration for flexibility with different environments.

For programmatic use, constructor injection is preferred:

java
// Minimal - uses default configuration
CodecResourceFactory factory = new CodecResourceFactory(metadataService);

// With custom configuration
CodecConfiguration config = CodecConfiguration.builder()
    .smartCompression(true)
    .typeStrategy(TypeStrategy.NAME)
    .build();
CodecResourceFactory factory = new CodecResourceFactory(metadataService, config);

// With custom Jackson mapper
JsonMapper.Builder mapperBuilder = JsonMapper.builder()
    .enable(SerializationFeature.INDENT_OUTPUT);
CodecResourceFactory factory = new CodecResourceFactory(metadataService, config, mapperBuilder);

9.2 Setter-Based Configuration (DI Frameworks)

For dependency injection frameworks (Spring, CDI, OSGi DS), use the parameterless constructor with setters:

java
// Parameterless constructor for DI
CodecResourceFactory factory = new CodecResourceFactory();

// Required: MetadataService must be set before creating resources
factory.setMetadataService(metadataService);

// Optional: Custom configuration (defaults to CodecConfiguration.defaults())
factory.setConfiguration(config);

// Optional: Custom Jackson mapper
factory.setMapperBuilder(mapperBuilder);

// Optional: Default load/save options
factory.setDefaultLoadOptions(loadOptions);
factory.setDefaultSaveOptions(saveOptions);

9.3 OSGi Declarative Services Example

java
@Component(service = Resource.Factory.class)
public class CodecResourceFactoryComponent extends CodecResourceFactory {

    @Reference
    public void setMetadataService(MetadataService metadataService) {
        super.setMetadataService(metadataService);
    }

    @Activate
    public void activate(Map<String, Object> properties) {
        // Build configuration from Config Admin properties
        CodecConfiguration config = CodecConfiguration.builder()
            .fromProperties(properties)
            .build();
        setConfiguration(config);
    }
}

9.4 Spring Configuration Example

java
@Configuration
public class CodecConfig {

    @Bean
    public CodecResourceFactory codecResourceFactory(MetadataService metadataService) {
        CodecResourceFactory factory = new CodecResourceFactory();
        factory.setMetadataService(metadataService);
        factory.setConfiguration(CodecConfiguration.builder()
            .smartCompression(true)
            .build());
        return factory;
    }
}

9.5 Default Options

Default load/save options are applied to all resources created by the factory:

java
Map<Object, Object> defaultLoadOptions = new HashMap<>();
defaultLoadOptions.put(CodecResource.CODEC_ROOT_SCHEMA, "http://example.org/1.0");

factory.setDefaultLoadOptions(defaultLoadOptions);

// All resources created by this factory will use these defaults
Resource resource = factory.createResource(uri);
resource.load(inputStream, null);  // Uses factory defaults

Per-operation options override factory defaults:

java
Map<Object, Object> operationOptions = new HashMap<>();
operationOptions.put(CodecResource.CODEC_ROOT_SCHEMA, "http://other.org/2.0");

resource.load(inputStream, operationOptions);  // Overrides factory default

10. Jackson Configuration

The JsonMapper.Builder can be provided at different levels:

LevelHow to ProvideScope
ResourceFactoryConstructor parameter or setMapperBuilder()All resources from factory
CodecResourceConstructor parameterSingle resource
DefaultnullJsonMapper.builder()Built-in default

Example:

java
JsonMapper.Builder customBuilder = JsonMapper.builder()
    .enable(SerializationFeature.INDENT_OUTPUT)
    .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);

CodecResourceFactory factory = new CodecResourceFactory(
    metadataService,
    codecConfig,
    customBuilder  // All resources use this base configuration
);

The codec applies its serializers/deserializers on top of the provided base configuration.


11. Property Matrix (Complete Reference)

This section provides the complete property matrix showing all configuration properties with their valid levels, directions, and defaults. This is the authoritative reference for the 3D configuration resolution.

11.1 Three-Dimensional Resolution

Configuration resolution operates across three dimensions:

  1. Sources (Vertical): Load/Save → Resource → ResourceFactory → Module → Annotation → Default
  2. Levels (Horizontal): Feature → EClass → Global → Default
  3. Direction: Serialization (write), Deserialization (read), or Both

For any property lookup:

  1. Level resolution (most specific wins): Feature → EClass → Global → Default
  2. Source resolution (highest priority wins): Options → ResourceFactory → Module → Annotation → Default
  3. Direction filter: Property must apply to current direction (read or write)

11.2 Legend

SymbolMeaning
GGlobal level
PEPackage level (annotation-internal, see §3.1)
CEClass level
FFeature (EAttribute/EReference) level
RRead direction (deserialization)
WWrite direction (serialization)
RWBoth directions
(R)WWrite primary, Read potential future feature
R(W)Read primary, Write potential future feature

11.3 Type Properties

PropertyLevelsDirectionDefaultSpec Section
typeStrategyG, P, C, FRWURI06-type.md
typeKeyG, P, C, FRW_type06-type.md
typeFormatG, P, C, FRWPLAIN06-type.md
typeSchemaKeyG, P, C, FRWschema06-type.md
typeNameKeyG, P, C, FRWtype06-type.md
typeScopeGRWALL06-type.md
typeFormatScopeGRWALL06-type.md
typeValueReaderNameG, CRnull06-type.md
typeValueWriterNameG, CWnull06-type.md
fingerprintModeG, P, CWNONE06-type.md §8
fingerprintKeyG, P, CW + R (read: caller-side levels only)fingerprint06-type.md §8

fingerprintKey read restriction: on the read side the key is taken from caller-side levels only (options, resource, factory, module), never from the model annotation, and the default key is always accepted in addition. This is not an inconsistency but the resolution of a genuine cycle — see 06-type.md §8.5.

11.4 ID Properties

PropertyLevelsDirectionDefaultSpec Section
idStrategyG, P, CRWID_FIELD09-id.md
idKeyG, P, C, FRW_id09-id.md
idValueKeyG, P, CRWid09-id.md
idFormatG, P, C, FRWPLAIN09-id.md
idKeyModeG, P, CRWID_ONLY09-id.md
idFeaturesP, CRW[]09-id.md
idSeparatorG, P, CRW-09-id.md
idSeparatorKeyG, P, CRWseparator09-id.md
idSeparatorSerializeG, C(R)Wtrue09-id.md
idOnTopG, P, C(R)Wfalse09-id.md
idScopeGRWALL09-id.md
idFormatScopeGRWALL09-id.md
idValueReaderNameG, P, CRnull09-id.md
idValueWriterNameG, P, CWnull09-id.md

11.5 Feature Properties

PropertyLevelsDirectionDefaultSpec Section
keyFRW(feature name)11-feature.md
ignoreG, C, FRWfalse11-feature.md
ignoreReadG, C, FRfalse11-feature.md
ignoreWriteG, C, FWfalse11-feature.md
forceReadG, C, FRfalse11-feature.md
forceWriteG, C, FWfalse11-feature.md
serializeNullG, C, F(R)Wfalse11-feature.md
serializeEmptyG, C, F(R)Wfalse11-feature.md
serializeDefaultG, C, F(R)Wfalse11-feature.md
enumSerializationG, FRWLITERAL11-feature.md
valueReaderNameG, C, FRnull14-custom-values.md
valueWriterNameG, C, FWnull14-custom-values.md

Note: enumSerialization applies to EAttributes only (not EReferences). Valid values: LITERAL, VALUE, NAME.

11.6 Reference Properties

PropertyLevelsDirectionDefaultSpec Section
refFormatG, FRWSTRUCTURED10-reference.md
refKeyG, FRW$ref10-reference.md
refTypeKeyG, FRW_type10-reference.md
proxyKeyG, FRW$proxy10-reference.md
expandG, C, FRWfalse10-reference.md
expandGlobalG, CRWfalse10-reference.md
expandDepthG, C, FRW110-reference.md
expandIgnoreBidirectionalG, C, F(R)Wtrue10-reference.md
serializeInstanceTypeG, FWtrue12-polymorphism.md

Note: serializeInstanceType=false is write-only and may cause deserialization failures if reference type is abstract/interface.

expand vs expandGlobal:

  • expand at F level: true/false - expand this specific reference
  • expand at G/C level: list of EReference names/instances to expand
  • expandGlobal: true/false - expand ALL non-containment references

A reference is expanded if expandGlobal=true OR the reference is in the expand list.

11.7 Annotation-Only Directives

These keys are valid only in EAnnotations and control how annotations are processed. They do NOT participate in the property resolution matrix.

Annotation KeyLevelsDefaultDescription
inheritG, CDIRECTControls EAnnotation inheritance across EClass hierarchy

inherit values:

  • DIRECT (default): Inherit from immediate parent EClass only
  • ALL: Inherit from full hierarchy up to EObject
  • NONE: No inheritance, use only this EClass's annotations

Note: inherit affects how CodecAspectProvider resolves annotations when building AspectConfig. It is consumed during annotation parsing, not during runtime property resolution.

⚠ Not yet implemented (code reality, 2026-07-24). Annotation inheritance across the EClass hierarchy is currently applied only to type configuration (ConfigurationResolver.resolveTypeConfig walks EClass.getEAllSuperTypes(), parents-first, child-overrides). It is not applied to id / feature / reference / class / discriminator config. The inherit modes DIRECT and NONE are not honored — the only behavior in code is the full transitive walk (equivalent to ALL), and the codec.inherit option constant has no consumers. Treat the inherit=DIRECT|NONE semantics and non-type-config inheritance as a documented future capability, not current behavior (convention as 99 §2.1).

11.8 Discriminator Mapping Properties

PropertyLevelsDirectionDefaultSpec Section
typeMapIdCRWnull08-discriminator-mapping.md
typeDiscriminatorPathCRWnull08-discriminator-mapping.md
typeDiscriminatorCRWnull08-discriminator-mapping.md
typeMappingsCRWnull08-discriminator-mapping.md
inlineMappingsFRWnull08-discriminator-mapping.md
discriminatorPathFRWnull08-discriminator-mapping.md
discriminatorValueFRWnull08-discriminator-mapping.md
fallbackStrategyG, C, FR(W)SKIP08-discriminator-mapping.md
fallbackEClassFR(W)null08-discriminator-mapping.md

11.10 SuperType Properties

PropertyLevelsDirectionDefaultSpec Section
superTypeSerializeG, P, C(R)Wfalse07-supertype.md
superTypeKeyG, P, C(R)W(format-dependent)¹07-supertype.md
superTypeStrategyG, P, C(R)WALL07-supertype.md
superTypeAsArrayG, P, C(R)Wtrue07-supertype.md
superTypeSeparatorG, P, C(R)W,07-supertype.md
superTypeFormatG, P, C(R)W(inherits typeFormat)07-supertype.md
superTypeValueReaderNameG, CRnull07-supertype.md
superTypeValueWriterNameG, CWnull07-supertype.md

¹ superTypeKey default: PLAIN format → _supertype, STRUCTURED format → supertype. Note: superTypeSchemaKey removed - inherits from TypeConfig. superTypeNameKey removed - superTypeKey has format-dependent default.

11.11 Strictness Properties

PropertyLevelsDirectionDefaultSpec Section
strictOnUnknownG, CRfalse11-feature.md
strictOnMissingG, CRfalse11-feature.md
deserializationModeG, CRLENIENT13-load-save-options.md

11.12 Global Options

PropertyLevelsDirectionDefaultSpec Section
smartCompressionGRWfalse05-global-options.md
ignoreFeaturesG, CRW[]05-global-options.md
fieldOrderG(R)WDECLARATION05-global-options.md
metadataFieldsFirstG(R)Wtrue05-global-options.md
useNamesFromExtendedMetadataGRWfalse11-feature.md

Note: useNamesFromExtendedMetadata is a runtime-only option (no EAnnotation support). When true, ExtendedMetaData name annotations are used as JSON keys (priority: codec annotation > ExtendedMetaData > feature name).

11.13 Load/Save Options (Runtime Only)

These options are only available at runtime via load/save option maps, not via EAnnotations.

PropertyLevelsDirectionDefaultSpec Section
rootTypeGRnull13-load-save-options.md
rootSchemaGRnull13-load-save-options.md
rootFingerprintGRnull13-load-save-options.md
featureTypeHintsGRnull13-load-save-options.md
typeHintModeGRHINT13-load-save-options.md
valueReadersGRnull13-load-save-options.md
valueWritersGWnull13-load-save-options.md
featureValueReadersGRnull13-load-save-options.md
featureValueWritersGWnull13-load-save-options.md
featureValueReaderInstancesGRnull13-load-save-options.md
featureValueWriterInstancesGWnull13-load-save-options.md

11.14 Direction Notes

Properties marked with (R)W or R(W) indicate:

  • (R)W: Write is primary use case, Read is potential future feature

    • idSeparatorSerialize: R needed to split concatenated ID back into components
    • idOnTop: R for ordered parsing/validation
    • serializeNull/Empty/Default: R for validation of intentional values
    • superType*: R for inheritance hierarchy validation
  • R(W): Read is primary use case, Write is potential future feature

    • fallbackStrategy/fallbackEClass: W needs read/write config separation design

12. Load/Save Option Keys Registry

This section documents all option keys that can be passed to resource.load(options) or resource.save(options).

11.1 Resource-Level Options

These options are defined in CodecResource:

Option KeyTypeDirectionDescription
CODEC_ROOT_TYPEEClass or String (URI)LoadType hint for root object deserialization
CODEC_ROOT_SCHEMAString (URI)LoadContext schema for NAME strategy resolution

Usage:

java
Map<String, Object> options = new HashMap<>();
options.put(CodecResource.CODEC_ROOT_TYPE, PersonPackage.Literals.PERSON);
options.put(CodecResource.CODEC_ROOT_SCHEMA, "http://example.org/person/1.0");
resource.load(inputStream, options);

11.2 Context Options

These options are defined in ContextHelper and affect the Jackson serialization/deserialization context:

Option KeyTypeDirectionDescription
CODEC_FEATURE_TYPE_HINTSMap<EStructuralFeature, EClass>LoadType hints for specific features
CODEC_FEATURE_VALUE_READERSMap<EStructuralFeature, String>LoadCustom value readers per feature
CODEC_FEATURE_VALUE_WRITERSMap<EStructuralFeature, String>SaveCustom value writers per feature

Usage:

java
Map<EStructuralFeature, EClass> typeHints = new HashMap<>();
typeHints.put(GeoPackage.Literals.FEATURE__GEOMETRY, GeoPackage.Literals.POINT);

Map<String, Object> options = new HashMap<>();
options.put(ContextHelper.FEATURE_TYPE_HINTS, typeHints);
resource.load(inputStream, options);

11.3 Internal Context Keys (Read-Only)

These keys are used internally by the codec and should not be set by users:

Option KeyTypeDescription
CODEC_EXPECTED_TYPEEClassCurrent expected type during deserialization
CODEC_UNRESOLVED_REFERENCESList<UnresolvedReference>Collected unresolved references
CODEC_DIAGNOSTIC_COLLECTORDiagnosticCollectorError/warning collection
CODEC_SUPPRESS_TYPEBooleanSmart compression type suppression flag
CODEC_CONTEXT_SCHEMA_URIStringCurrent context schema for smart compression
CODEC_ROOT_SERIALIZEDBooleanRoot object serialization complete flag
CODEC_FEATURE_TYPE_HINTEClassCurrent feature's type hint

11.4 Property Map Format

For configuration via property maps (OSGi Config Admin, Spring, etc.), keys follow this pattern:

codec.<target>.<property>

Examples:

properties
# Global settings
codec.smartCompression=true
codec.sortPropertiesAlphabetically=false

# Type settings
codec.type.strategy=NAME
codec.type.key=_type

# ID settings
codec.id.enabled=true
codec.id.onTop=true
codec.id.key=_id

# Per-class settings (using fully qualified class name)
codec.Person.type.strategy=URI
codec.Person.id.features=firstName,lastName

# Per-feature settings
codec.Person.firstName.key=first_name
codec.Person.firstName.serialize=true

⚠ Per-class / per-feature string addressing is not yet implemented (code reality, 2026-07-24). The dotted codec.<ClassName>.<property> and codec.<ClassName>.<feature>.<property> forms shown above are not parsed into per-class/per-feature config by any current source (no hierarchical key splitting exists). Per-class/per-feature caller config today is supplied only through the instance-keyed channels (CODEC_ECLASS_CONFIG, CODEC_EREFERENCE_CONFIG, CODEC_EATTRIBUTE_CONFIG — keyed by EClass/ EReference/EAttribute instance) or via model annotations. When the string form is implemented, it is version-agnostic by design (applies to all versions of a class name) — see the fingerprinting workdoc A.5. Until then, treat these examples as illustrative of the intended key shape, not as a working feature.

11.5 Java Constants

All option keys are available as Java constants:

java
import org.eclipse.fennec.codec.resource.CodecResource;
import org.eclipse.fennec.codec.context.ContextHelper;

// Resource options
CodecResource.CODEC_ROOT_TYPE
CodecResource.CODEC_ROOT_SCHEMA

// Context options
ContextHelper.FEATURE_TYPE_HINTS
ContextHelper.FEATURE_VALUE_READERS
ContextHelper.FEATURE_VALUE_WRITERS

11.6 Type-Safe Configuration (Preferred)

For most use cases, the type-safe CodecConfiguration.Builder is preferred over raw option maps:

java
CodecConfiguration config = CodecConfiguration.builder()
    // Global settings
    .smartCompression(true)
    .typeStrategy(TypeStrategy.NAME)

    // ID settings
    .idKeyMode(IdKeyMode.ID_ONLY)
    .idOnTop(true)
    .idKey("_id")

    // Per-class overrides
    .forClass(PersonPackage.Literals.PERSON)
        .typeStrategy(TypeStrategy.URI)
        .idFeatures("firstName", "lastName")
        .done()

    .build();

CodecResourceFactory factory = new CodecResourceFactory(metadataService, config);

The builder approach provides:

  • Compile-time type safety
  • IDE auto-completion
  • Validation of dependent settings

11.7 Direction-Specific Configuration

Properties can be scoped to serialization, deserialization, or both using prefixes:

PrefixApplies ToExample
codec.Both ser and desercodec.typeStrategy=NAME
codec.ser.Serialization onlycodec.ser.typeStrategy=NAME
codec.deser.Deserialization onlycodec.deser.typeStrategy=URI

Use Case: Different strategies for serialization vs deserialization:

java
Map<String, Object> properties = Map.of(
    "codec.ser.typeStrategy", "NAME",      // Serialize with simple names
    "codec.deser.typeStrategy", "URI"      // Deserialize expects full URIs
);

// Build separate configs
CodecConfiguration serConfig = ConfigBuilder.fromMap(properties).buildSerializationConfig();
// serConfig.typeStrategy = NAME

CodecConfiguration deserConfig = ConfigBuilder.fromMap(properties).buildDeserializationConfig();
// deserConfig.typeStrategy = URI

Precedence: Specific prefix overrides general prefix:

java
Map<String, Object> properties = Map.of(
    "codec.typeStrategy", "URI",           // Default for both
    "codec.ser.typeStrategy", "NAME"       // Override for serialization only
);
// Serialization: NAME (specific override)
// Deserialization: URI (general default)

When to use direction-specific configuration:

  • External API writes URIs but you want to serialize simple names
  • Legacy system compatibility (read old format, write new format)
  • Testing/migration scenarios

EAnnotation alternative: Direction-specific configuration is primarily a runtime concern and is not supported via EAnnotations. Use property maps or CodecConfiguration builder for direction-specific settings.


Next: Naming Conventions →

Released under the EPL-2.0 License. Eclipse Fennec is part of the Eclipse Foundation.