This document describes the serialization format for LionWeb chunks.
Conventions used in this document
-
italic words refer to concepts defined by JSON.
-
bold words refer to concepts defined in this document.
-
monospaced
words describe verbatim contents of the serialization. -
ALL-CAPS key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP14 (RFC2119, RFC8174) when, and only when, they appear in all capitals, as shown here.
-
"processed document" refers to the character sequence that’s parsed or written, e.g. a file or network stream.
-
Footnotes refer to more discussions and rationale, but are non-normative.
Design goals
We want to provide boring and proven infrastructure, so that innovation can be built on top of it. We do not take any measures to reduce the amount of transmitted data.[1] We strive for shallow structures to enable stream-based processing.
Description
Overview of structures
No unspecified members are allowed anywhere in the structure.[3]
Root structure
Root level MUST be an object with three members, called serialization chunk.
The first member SHOULD be key serializationFormatVersion
with a string value.[4][2][5]
The value MUST be a non-empty string (without leading or trailing whitespace)[6] describing the serialization format version used to create the processed document, according to Versions.
The second member SHOULD be key languages
with an array value.[7][8][2]
Each element in the value array MUST adhere to Language structure.
The order of elements is undefined.
elements MUST contain all language/version referred to by any Meta-pointer in the processed document.
Each element MUST be unique with respect to all its members.
The third member SHOULD be key nodes
with an array value.[9][2]
Each element in the value array MUST adhere to Node structure.
The order of elements is undefined.
Each element MUST be unique with respect to the value of its key id
.
Language structure
Each used language MUST be an object.[10] The order of members is undefined.
Note
|
If the chunk describes a language (M2), it might include instances of builtins' language entities. In this case, builtins MUST be listed as used language like any other language.[11] |
The object MUST contain the following members:[7]
-
key
key
with string value, adhering to Language key. -
key
version
with string value, adhering to Language version.
Meta-pointer
A meta-pointer is a reference from M1 to M2.[15][16] It’s used at several places within Node structure.
Each meta-pointer MUST be an object. The order of members is undefined.
The object MUST contain the following members:
-
key
language
with string value, adhering to Language key. -
key
version
with string value, adhering to Language version. -
key
key
with string value according to Keys spec. Refers to some element in the language. Which element exactly is specified for each usage of meta-pointer.
Node structure
Each node MUST be an object.[17] The order of members is undefined.
-
key
id
with string value, adhering to Id value. -
key
classifier
[21][22] with object value, adhering to Meta-pointer. The meta-pointer'skey
's value refers to the key of the Concept or Annotation this node is an instance of. -
key
properties
with array value, each element adhering to Property. The order of elements is undefined.[23] -
key
containments
[24][25] with array value, each element adhering to Containment. The order of elements is undefined.[23] -
key
references
[26] with array value, each element adhering to Reference. The order of elements is undefined.[23] -
key
annotations
[27] with array value, each element adhering to Annotation. The order of elements MUST be maintained.[28] -
key
parent
[29] with string or null value, adhering to Parent.
For all features (i.e. properties
, containments
, and references
) defined for a node's classifier:[9]
-
During serialization, we SHOULD include every feature, even if unset. In the latter case:
-
the property MUST have
value
= null; -
the containment MUST have
children
= empty array; -
the reference MUST have
targets
= empty array;
-
-
During deserialization, we MUST accept a node even if not all defined features are present.
-
We MAY accept undefined features during deserialization if we can deal with them in a meaningful manner.
Note
|
We currently cannot store "invalid text" (i.e. user-entered text that does not adhere to the underlying structure and/or constraints) in the model. We will support this in a future release.[30] |
Id value
A string according to Identifier spec. Defines the id of this node.
Property
Each property MUST be an object. The order of members is undefined.
The object MUST contain the following members:
-
key
property
with object value, adhering to Meta-pointer. The meta-pointer'skey
's value refers to the key of the Property this property is an instance of. -
key
value
with value as one of-
string[31] containing the value of the property referenced by the
property
. Refer to Property serialization for the specification of the value format. Can be an empty string. -
null to explicitly specify the property to be unset.
-
Containment
Each containment MUST be an object. The order of members is undefined.
The object MUST contain the following members:
-
key
containment
with object value, adhering to Meta-pointer. The meta-pointer'skey
's value refers to the key of the Containment this containment is an instance of. -
key
children
with array value with string elements. Each element adheres to Identifier spec, and refers to the id of the contained node. The order of elements MUST be maintained.[28]NoteEach children
element is the inverse relation of a parent.NoteThe children
node can be contained in the processed document, but also can be outside the processed document (i.e. not contained in the processed document).
Reference
Each reference MUST be an object. The order of members is undefined.
The object MUST contain the following members:
-
key
reference
with object value, adhering to Meta-pointer. The meta-pointer'skey
's value refers to the key of the Reference this reference is an instance of. -
key
targets
with array value with _object elements. The order of elements MUST be maintained.[28] Each element MUST have the following members in undefined order:[26]-
key
resolveInfo
[32] with value as one of:-
string containing resolveInfo, a textual hint that might be used to find the target node of this reference. Interface INamed SHOULD be used as a default, if available. The exact value depends on the implementation. Can be an empty string.
-
null if no resolveInfo is available.
-
-
key
reference
[33] with value as one of:-
string according to Identifier spec. Refers to the id of the target node.
NoteThe referred node can be contained in the processed document, but also can be outside the processed document (i.e. not contained in the processed document). -
null if the id of the target node is not known.
-
-
Annotation
Each annotation MUST be a string. It adheres to Identifier spec, and refers to the id of the contained annotation node.
Note
|
Each annotation element is the inverse relation of a parent. |
Note
|
The annotation node can be contained in the processed document, but also can be outside the processed document (i.e. not contained in the processed document). |
Parent
One of
-
string according to Identifier spec. Refers to the id of the node containing this node.
Noteparent is the inverse relation of either one containment or one annotation.[34] NoteThe referred node can be contained in the processed document, but also can be outside the processed document (i.e. not contained in the processed document). -
null if
-
This node is a root node, i.e. this node does not have a parent.
-
This serialization is sent as an update request.
-
Property serialization
All property values MUST be serialized as JSON string.[31][35]. An unset property SHOULD be serialized as JSON null.
String
LionCore Strings might be any string, of any length, including (but not limited to):
-
empty string:
""
-
only containing whitespace:
" "
-
containing escaped characters as per JSON spec:
"They said:\n \"Hello!\""
-
containing extended Unicode characters:
"😐"
-
containing escaped Unicode characters:
"\uD83D\uDE10"
Boolean
LionCore Booleans MUST be encoded as exactly one of these JSON strings:
-
"true"
-
"false"
Booleans MUST NOT be encoded with leading or trailing whitespace, uppercase characters, short forms (like t
or f
), or decimal representation (like 1
, 0
, -1
).
Integer
LionCore Integers MUST be encoded as JSON string.
-
Integers MUST be represented in base-10.
-
The digits can be prefixed with either
+
(plus) or-
(minus).[36] -
Integers MUST NOT be prefixed by leading zeros.
-
Integers can contain value zero with any prefix, i.e.
0
,-0
, or+0
. -
Integers MUST NOT contain leading or trailing whitespace.
-
LionWeb does NOT limit the range of the integer value.[37] An implementation MAY refuse a model containing an integer value outside the supported range.
-
"0"
-
"+0"
-
"-0"
-
"123"
-
"-100000"
-
"+999"
-
"100000000200000000300000000400000000500000000600000000700000000800000000900000000999999999"
-
"-999999999900000000800000000700000000600000000500000000400000000300000000200000000100000000"
-
""
-
123
-
-1
-
"+-0"
-
"++1"
-
"00002"
-
"0xAA12"
-
" 5"
-
"-6 "
JSON
LionCore JSON MUST be encoded as JSON string. All double quotes, line breaks, etc. MUST be escaped to form a proper JSON string. The value MUST adhere to JSON spec (RFC 8259).
"{ \"key\": \"my value\",\n\"myArray\": [1, -2, true] }"
{ "key": "my value", "myArray": [1, -2, true] }
Enumeration literals
LionCore Enumeration literals MUST be encoded as JSON string value according to Key spec. MUST refer to the key of an EnumerationLiteral of the Enumeration defined as type of this Property.[12]
Examples
Minimal
{
"serializationFormatVersion": "2023.1",
"languages": [],
"nodes": []
}
Minimal node
{
"serializationFormatVersion": "2023.1",
"languages": [
{
"key": "myLanguage",
"version": "2"
}
],
"nodes": [
{
"id": "aaa",
"classifier": {
"language": "myLanguage",
"version": "2",
"key": "myConceptId"
},
"properties": [],
"containments": [],
"references": [],
"annotations": [],
"parent": null
}
]
}
Property variants
For this example, we need to define an enumeration and a concept that uses the enumeration.
Note
|
The format used in this example is non-normative. |
Assume this enumeration:
enumeration DaysOfWeek [id 23, key days-of-week]
literal Monday [id 34, key monday]
literal Tuesday [id 2, key tttt]
literal Wednesday [id 55, key 12398712]
And this concept:
concept OpeningTime [id 44, key time_to_open]
property day: DaysOfWeek [id 42, key day]
property startHour: Integer [id 22, key starthour]
property endHour: Integer [id 89, key endhour]
{
"serializationFormatVersion": "2023.1",
"languages": [
{
"key": "myLanguage",
"version": "2"
}
],
"nodes": [
{
"id": "bbb",
"classifier": {
"language": "myLanguage",
"version": "2",
"key": "myConceptId"
},
"properties": [
{
"property": {
"language": "myLanguage",
"version": "2",
"key": "stringPropertyId"
},
"value": "my string value"
},
{
"property": {
"language": "myLanguage",
"version": "2",
"key": "integerPropertyId"
},
"value": "123"
},
{
"property": {
"language": "myLanguage",
"version": "2",
"key": "booleanPropertyId"
},
"value": "true"
},
{
"property": {
"language": "myLanguage",
"version": "2",
"key": "jsonPropertyId"
},
"value": "{ \"name\": \"Bob\" }"
},
{
"property": {
"language": "myLanguage",
"version": "2",
"key": "unsetPropertyId"
},
"value": null
}
],
"containments": [],
"references": [],
"annotations": [],
"parent": null
},
{
"id": "21",
"classifier": {
"language": "myLanguage",
"version": "2",
"key": "time_to_open"
},
"properties": [
{
"property": {
"language": "myLanguage",
"version": "2",
"key": "day"
},
"value": "tttt"
},
{
"property": {
"language": "myLanguage",
"version": "2",
"key": "starthour"
},
"value": "9"
},
{
"property": {
"language": "myLanguage",
"version": "2",
"key": "endhour"
},
"value": "5"
}
],
"containments": [],
"references": [],
"annotations": [],
"parent": null
}
]
}
Containment variants
{
"serializationFormatVersion": "2023.1",
"languages": [
{
"key": "myLanguage",
"version": "2"
}
],
"nodes": [
{
"id": "ccc",
"classifier": {
"language": "myLanguage",
"version": "2",
"key": "myConceptId"
},
"properties": [],
"containments": [
{
"containment": {
"language": "myLanguage",
"version": "2",
"key": "emptyContainmentId"
},
"children": []
},
{
"containment": {
"language": "myLanguage",
"version": "2",
"key": "singleContainmentId"
},
"children": [
"cdd"
]
},
{
"containment": {
"language": "myLanguage",
"version": "2",
"key": "multiContainmentId"
},
"children": [
"cee",
"cff",
"cgg"
]
}
],
"references": [],
"annotations": [],
"parent": null
},
{
"id": "cgg",
"classifier": {
"language": "myLanguage",
"version": "2",
"key": "differentConceptId"
},
"properties": [],
"containments": [],
"references": [],
"annotations": [],
"parent": null
},
{
"id": "cdd",
"classifier": {
"language": "myLanguage",
"version": "2",
"key": "otherConceptId"
},
"properties": [],
"containments": [],
"references": [],
"annotations": [],
"parent": "ccc"
},
{
"id": "cee",
"classifier": {
"language": "myLanguage",
"version": "2",
"key": "differentConceptId"
},
"properties": [],
"containments": [],
"references": [],
"annotations": [],
"parent": null
}
]
}
node with id cff
is outside the processed document.
Reference variants
We support different kinds of targets.[38]
{
"serializationFormatVersion": "2023.1",
"languages": [
{
"key": "myLanguage",
"version": "2"
}
],
"nodes": [
{
"id": "ddd",
"classifier": {
"language": "myLanguage",
"version": "2",
"key": "myConceptId"
},
"properties": [],
"containments": [],
"references": [
{
"reference": {
"language": "myLanguage",
"version": "2",
"key": "emptyReferenceId"
},
"targets": []
},
{
"reference": {
"language": "myLanguage",
"version": "2",
"key": "singleReferenceId"
},
"targets": [
{
"resolveInfo": "some name",
"reference": "dee"
}
]
},
{
"reference": {
"language": "myLanguage",
"version": "2",
"key": "multiReferenceId"
},
"targets": [
{
"resolveInfo": "self-reference",
"reference": "ddd"
},
{
"resolveInfo": "only resolve info",
"reference": null
}
]
},
{
"reference": {
"language": "myLanguage",
"version": "2",
"key": "noResolveInfoReferenceId"
},
"targets": [
{
"resolveInfo": null,
"reference": "dee"
}
]
},
{
"reference": {
"language": "myLanguage",
"version": "2",
"key": "neitherResolveInfoNorReferenceId"
},
"targets": [
{
"resolveInfo": null,
"reference": null
}
]
}
],
"annotations": [],
"parent": null
},
{
"id": "dee",
"classifier": {
"language": "myLanguage",
"version": "2",
"key": "differentConceptId"
},
"properties": [],
"containments": [],
"references": [],
"annotations": [],
"parent": null
}
]
}
Annotation variants
For this example, we need to define some annotations and their annotated concepts.
Note
|
The format used in this example is non-normative. |
Assume these annotations:
annotation Docu [id 23, key docuAnn]
multiple = true
annotates = IDocumentable
property docu: String [id 34, key Docu-docu]
annotation ExtendedDocu extends Docu [id 20, key docuExtended]
property moreDocu: String [id mds, key MDS]
annotation Marker [id 22, key myMarker]
multiple = false
annotates = Node
annotation TrashCan [id 99, key throwAway]
multiple = false
annotates = Node
containment trash: 0..* Node [id 2, key tat]
interface JavaInfo [id 33, key jv]
property javaVersion: String [id 33a, key jvA]
annotation MappedToClass implements JavaInfo [id mtc, key MTC]
multiple = false
annotates = Classifier
reference javaClass: 1 BaseLanguageClass [id jjj, key JJJ]
annotation UsesMapping implements JavaInfo [id um, key UM]
multiple = false
annotates = Feature
reference mapping: 1 MappedToClass [id jjj1, key JJJ1]
And these concepts:
interface IDocumentable [id 50, key 51]
concept BaseLanguageClass implements IDocumentable [id 60, key 61]
concept OtherLanguageConcept [id 70, key otherLangConc]
reference usesTrashCan: 0..1 TrashCan [id 72, key usesTrashCan]
{
"serializationFormatVersion": "2023.1",
"languages": [
{
"key": "myLanguage",
"version": "2"
},
{
"key": "BaseLanguage",
"version": "1"
},
{
"key": "LionWeb-M3",
"version": "2023.1"
}
],
"nodes": [
{
"id": "ccc",
"classifier": {
"language": "myLanguage",
"version": "2",
"key": "61"
},
"properties": [],
"containments": [],
"references": [],
"annotations": [
"marker",
"docu1",
"docu2",
"localTrash"
],
"parent": null
},
{
"id": "marker",
"classifier": {
"language": "myLanguage",
"version": "2",
"key": "myMarker"
},
"properties": [],
"containments": [],
"references": [],
"annotations": [],
"parent": "61"
},
{
"id": "docu1",
"classifier": {
"language": "myLanguage",
"version": "2",
"key": "docuAnn"
},
"properties": [
{
"property": {
"language": "myLanguage",
"version": "2",
"key": "Docu-docu"
},
"value": "This is a very important BaseLanguageClass"
}
],
"containments": [],
"references": [],
"annotations": [],
"parent": "61"
},
{
"id": "docu2",
"classifier": {
"language": "myLanguage",
"version": "2",
"key": "docuExtended"
},
"properties": [
{
"property": {
"language": "myLanguage",
"version": "2",
"key": "Docu-docu"
},
"value": "We want to say a few more things about this BaseLanguageClass"
},
{
"property": {
"language": "myLanguage",
"version": "2",
"key": "MDS"
},
"value": "Here be dragons"
}
],
"containments": [],
"references": [],
"annotations": [],
"parent": "61"
},
{
"id": "localTrash",
"classifier": {
"language": "myLanguage",
"version": "2",
"key": "throwAway"
},
"properties": [],
"containments": [
{
"containment": {
"language": "myLanguage",
"version": "2",
"key": "tat"
},
"children": [
"old1",
"old2"
]
}
],
"references": [],
"annotations": [],
"parent": "61"
},
{
"id": "old1",
"classifier": {
"language": "myLanguage",
"version": "2",
"key": "SomeConcept"
},
"properties": [],
"containments": [],
"references": [],
"annotations": [],
"parent": "localTrash"
},
{
"id": "old2",
"classifier": {
"language": "myLanguage",
"version": "2",
"key": "YetAnotherConcept"
},
"properties": [],
"containments": [],
"references": [],
"annotations": [],
"parent": "localTrash"
},
{
"id": "bbb",
"classifier": {
"language": "LionWeb-M3",
"version": "2023.1",
"key": "Concept"
},
"properties": [],
"containments": [
{
"containment": {
"language": "LionWeb-M3",
"version": "2023.1",
"key": "Classifier-features"
},
"children": [
"bbb-prop"
]
}
],
"references": [],
"annotations": [
"javaMapping"
],
"parent": null
},
{
"id": "bbb-prop",
"classifier": {
"language": "LionWeb-M3",
"version": "2023.1",
"key": "Property"
},
"properties": [],
"containments": [],
"references": [],
"annotations": [
"typeUseMapping"
],
"parent": "bbb"
},
{
"id": "javaMapping",
"classifier": {
"language": "myLanguage",
"version": "2",
"key": "MTC"
},
"properties": [],
"containments": [],
"references": [
{
"reference": {
"language": "myLanguage",
"version": "2",
"key": "JJJ"
},
"targets": [
{
"resolveInfo": null,
"reference": "javaClass"
}
]
}
],
"annotations": [],
"parent": "bbb"
},
{
"id": "typeUseMapping",
"classifier": {
"language": "myLanguage",
"version": "2",
"key": "UM"
},
"properties": [],
"containments": [],
"references": [
{
"reference": {
"language": "myLanguage",
"version": "2",
"key": "JJJ1"
},
"targets": [
{
"resolveInfo": null,
"reference": "javaMapping"
}
]
}
],
"annotations": [],
"parent": "bbb-prop"
},
{
"id": "javaClass",
"classifier": {
"language": "BaseLanguage",
"version": "1",
"key": "ClassConcept"
},
"properties": [],
"containments": [],
"references": [],
"annotations": [],
"parent": null
}
]
}
Versions
Note
|
The term "version" is a bit ambiguous within LionWeb.[5]
This section lists all versions as they appear in serializationFormatVersion and Language.version for languages LionWeb_M3 and LionWeb_builtins .
|
JSON Schema for serialization
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://lionweb.io/serialization.schema.json",
"title": "LionWeb Serialization",
"type": "object",
"properties": {
"serializationFormatVersion": {
"type": "string",
"pattern": "^\\S+(.*\\S)?$"
},
"languages": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"$ref": "#/$defs/key"
},
"version": {
"$ref": "#/$defs/version"
}
},
"required": [
"key",
"version"
],
"additionalProperties": false,
"minProperties": 2,
"maxProperties": 2
},
"uniqueItems": true
},
"nodes": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"$ref": "#/$defs/id"
},
"classifier": {
"$ref": "#/$defs/metaPointer"
},
"properties": {
"type": "array",
"items": {
"type": "object",
"properties": {
"property": {
"$ref": "#/$defs/metaPointer"
},
"value": {
"oneOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": [
"property",
"value"
],
"additionalProperties": false,
"minProperties": 2,
"maxProperties": 2
}
},
"containments": {
"type": "array",
"items": {
"type": "object",
"properties": {
"containment": {
"$ref": "#/$defs/metaPointer"
},
"children": {
"type": "array",
"items": {
"$ref": "#/$defs/id"
},
"uniqueItems": true
}
},
"required": [
"containment",
"children"
],
"additionalProperties": false,
"minProperties": 2,
"maxProperties": 2
}
},
"references": {
"type": "array",
"items": {
"type": "object",
"properties": {
"reference": {
"$ref": "#/$defs/metaPointer"
},
"targets": {
"type": "array",
"items": {
"type": "object",
"properties": {
"resolveInfo": {
"oneOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"reference": {
"oneOf": [
{
"$ref": "#/$defs/id"
},
{
"type": "null"
}
]
}
},
"required": [
"resolveInfo",
"reference"
],
"additionalProperties": false,
"minProperties": 2,
"maxProperties": 2
}
}
},
"required": [
"reference",
"targets"
],
"additionalProperties": false,
"minProperties": 2,
"maxProperties": 2
}
},
"annotations": {
"type": "array",
"items": {
"$ref": "#/$defs/id"
},
"uniqueItems": true
},
"parent": {
"oneOf": [
{
"$ref": "#/$defs/id"
},
{
"type": "null"
}
]
}
},
"required": [
"id",
"classifier",
"properties",
"containments",
"references",
"annotations",
"parent"
],
"additionalProperties": false,
"minProperties": 7,
"maxProperties": 7
},
"uniqueItems": true
}
},
"required": [
"serializationFormatVersion",
"languages",
"nodes"
],
"additionalProperties": false,
"minProperties": 3,
"maxProperties": 3,
"$defs": {
"id": {
"type": "string",
"minLength": 1,
"pattern": "^[a-zA-Z0-9_-]+$"
},
"key": {
"$ref": "#/$defs/id"
},
"version": {
"type": "string",
"minLength": 1
},
"metaPointer": {
"type": "object",
"properties": {
"language": {
"$ref": "#/$defs/key"
},
"version": {
"$ref": "#/$defs/version"
},
"key": {
"$ref": "#/$defs/key"
}
},
"required": [
"language",
"version",
"key"
],
"additionalProperties": false,
"minProperties": 3,
"maxProperties": 3
}
}
}