Repository navigation
Description
We currently have two phases in serialization and deserialization:
- constants
- recordings
However, we are going to add more phases:
- loading units ([record_use] Dart API: Loading units as object/class #2979)
- definitions ([record_use] Correctness: Support nested const instances #2867)
- constants (will require definitions after the above change)
- recordings (requires all 3 above)
In order to keep the code clean we should explicitly document/implement the order of serialization/deserialization. Currently the List<Constant> (map int->constant) for deserialization and Map<Constant, int> for serialization are passed around explicitly. This will become a mess if we get 3 context params threaded through the serialization/deserialization.
(N.b., I explored adding references to the json_syntax_generator but that would make the json syntax context-dependent instead of context-free leading to having that layer having to deal with deduplication of objects etc. That seems to be the wrong layer of abstraction.)
Sketch of how it could work:
// Copyright (c) 2026, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
/// This library defines the context objects used for progressive serialization
/// and deserialization of normalized JSON.
///
/// ### Layer Responsibilities
///
/// The **Syntax Layer** is designed to be context-free, acting as a thin wrapper
/// around JSON data. This aligns with the capabilities of standard JSON Schema,
/// which excels at structural validation but does not natively express
/// relational dependencies between disparate branches of a tree.
///
/// The **Semantic Layer** is responsible for identity and relationship
/// resolution. It manages these [DeserializationContext] and
/// [SerializationContext] objects to "thread" the reference pools through the
/// resolution pipeline, ensuring that normalized indices are correctly mapped
/// to (and from) deduplicated semantic objects.
///
/// ### Resolution Order
///
/// The resolution order is identical for both serialization and
/// deserialization, ensuring that all dependencies are available at each step:
///
/// 1. **Loading Units**: Context-free.
/// 2. **Definitions**: Resolved second (available for Constants).
/// 3. **Constants**: Resolved third. May contain references to the
/// Definitions pool.
/// 4. **Recordings**: Resolved last. References Loading Units, Definitions,
/// and Constants.
library;
import 'constant.dart';
import 'definition.dart';
import 'recordings.dart';
import 'reference.dart';
// Note: LoadingUnit is currently represented as a String in the API,
// but will be moved to an object in future iterations.
// For now, we use a placeholder or the existing type.
typedef LoadingUnit = String;
/// Initial state of deserialization where no pools have been resolved.
///
/// This serves as the base class for all deserialization contexts.
base class DeserializationContext {
const DeserializationContext();
}
/// Context providing access to the resolved [LoadingUnit] pool.
base class LoadingUnitDeserializationContext extends DeserializationContext {
final List<LoadingUnit> loadingUnits;
const LoadingUnitDeserializationContext({required this.loadingUnits});
}
/// Context providing access to both [LoadingUnit] and [Definition] pools.
base class DefinitionDeserializationContext
extends LoadingUnitDeserializationContext {
final List<Definition> definitions;
const DefinitionDeserializationContext({
required super.loadingUnits,
required this.definitions,
});
factory DefinitionDeserializationContext.fromLoadingUnits(
LoadingUnitDeserializationContext prev,
List<Definition> definitions,
) =>
DefinitionDeserializationContext(
loadingUnits: prev.loadingUnits,
definitions: definitions,
);
}
/// The final deserialization state where all pools are resolved.
///
/// This context is used to resolve the final [Recordings] and their
/// [Reference]s.
final class FullDeserializationContext
extends DefinitionDeserializationContext {
final List<Constant> constants;
const FullDeserializationContext({
required super.loadingUnits,
required super.definitions,
required this.constants,
});
factory FullDeserializationContext.fromDefinitions(
DefinitionDeserializationContext prev,
List<Constant> constants,
) =>
FullDeserializationContext(
loadingUnits: prev.loadingUnits,
definitions: prev.definitions,
constants: constants,
);
}
/// Initial state for serialization before any maps have been built.
///
/// This serves as the base class for all serialization contexts.
base class SerializationContext {
const SerializationContext();
}
/// Context providing access to the full [LoadingUnit] index map.
base class LoadingUnitSerializationContext extends SerializationContext {
final Map<LoadingUnit, int> loadingUnits;
const LoadingUnitSerializationContext({required this.loadingUnits});
}
/// Context providing access to both [LoadingUnit] and [Definition] index maps.
base class DefinitionSerializationContext
extends LoadingUnitSerializationContext {
final Map<Definition, int> definitions;
const DefinitionSerializationContext({
required super.loadingUnits,
required this.definitions,
});
factory DefinitionSerializationContext.fromLoadingUnits(
LoadingUnitSerializationContext prev,
Map<Definition, int> definitions,
) =>
DefinitionSerializationContext(
loadingUnits: prev.loadingUnits,
definitions: definitions,
);
}
/// The final serialization state where all index maps are available.
///
/// This context is used to convert the final [Recordings] and their
/// [Reference]s to syntax.
final class FullSerializationContext extends DefinitionSerializationContext {
final Map<Constant, int> constants;
const FullSerializationContext({
required super.loadingUnits,
required super.definitions,
required this.constants,
});
factory FullSerializationContext.fromDefinitions(
DefinitionSerializationContext prev,
Map<Constant, int> constants,
) =>
FullSerializationContext(
loadingUnits: prev.loadingUnits,
definitions: prev.definitions,
constants: constants,
);
}Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsDone
Activity
[record_use] Normalize definitions in JSON (#3109)
Squashed commit of the following: