lutaml-model 0.8.4 → 0.8.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/dependent-tests.yml +3 -1
  3. data/.rubocop.yml +18 -0
  4. data/.rubocop_todo.yml +12 -18
  5. data/Gemfile +2 -0
  6. data/README.adoc +114 -2
  7. data/docs/_guides/index.adoc +18 -0
  8. data/docs/_guides/jsonld-serialization.adoc +217 -0
  9. data/docs/_guides/rdf-serialization.adoc +344 -0
  10. data/docs/_guides/turtle-serialization.adoc +224 -0
  11. data/docs/_pages/serialization_adapters.adoc +31 -0
  12. data/docs/_references/index.adoc +1 -0
  13. data/docs/_references/rdf-namespaces.adoc +243 -0
  14. data/docs/index.adoc +3 -2
  15. data/lib/lutaml/jsonld/adapter.rb +23 -0
  16. data/lib/lutaml/jsonld/context.rb +69 -0
  17. data/lib/lutaml/jsonld/term_definition.rb +39 -0
  18. data/lib/lutaml/jsonld/transform.rb +174 -0
  19. data/lib/lutaml/jsonld.rb +23 -0
  20. data/lib/lutaml/model/format_registry.rb +10 -1
  21. data/lib/lutaml/model/serialize/format_conversion.rb +17 -1
  22. data/lib/lutaml/model/version.rb +1 -1
  23. data/lib/lutaml/model.rb +6 -0
  24. data/lib/lutaml/rdf/error.rb +7 -0
  25. data/lib/lutaml/rdf/iri.rb +44 -0
  26. data/lib/lutaml/rdf/language_tagged.rb +11 -0
  27. data/lib/lutaml/rdf/literal.rb +62 -0
  28. data/lib/lutaml/rdf/mapping.rb +71 -0
  29. data/lib/lutaml/rdf/mapping_rule.rb +35 -0
  30. data/lib/lutaml/rdf/member_rule.rb +13 -0
  31. data/lib/lutaml/rdf/namespace.rb +58 -0
  32. data/lib/lutaml/rdf/namespace_set.rb +69 -0
  33. data/lib/lutaml/rdf/namespaces/dcterms_namespace.rb +12 -0
  34. data/lib/lutaml/rdf/namespaces/owl_namespace.rb +12 -0
  35. data/lib/lutaml/rdf/namespaces/rdf_namespace.rb +14 -0
  36. data/lib/lutaml/rdf/namespaces/rdfs_namespace.rb +12 -0
  37. data/lib/lutaml/rdf/namespaces/skos_namespace.rb +12 -0
  38. data/lib/lutaml/rdf/namespaces/xsd_namespace.rb +12 -0
  39. data/lib/lutaml/rdf/namespaces.rb +14 -0
  40. data/lib/lutaml/rdf/transform.rb +36 -0
  41. data/lib/lutaml/rdf.rb +19 -0
  42. data/lib/lutaml/turtle/adapter.rb +35 -0
  43. data/lib/lutaml/turtle/mapping.rb +7 -0
  44. data/lib/lutaml/turtle/transform.rb +158 -0
  45. data/lib/lutaml/turtle.rb +22 -0
  46. data/spec/lutaml/integration/edge_cases_spec.rb +109 -0
  47. data/spec/lutaml/integration/multi_format_spec.rb +106 -0
  48. data/spec/lutaml/integration/round_trip_spec.rb +170 -0
  49. data/spec/lutaml/jsonld/adapter_spec.rb +46 -0
  50. data/spec/lutaml/jsonld/context_spec.rb +114 -0
  51. data/spec/lutaml/jsonld/term_definition_spec.rb +55 -0
  52. data/spec/lutaml/jsonld/transform_spec.rb +211 -0
  53. data/spec/lutaml/rdf/graph_serialization_spec.rb +137 -0
  54. data/spec/lutaml/rdf/iri_spec.rb +73 -0
  55. data/spec/lutaml/rdf/literal_spec.rb +98 -0
  56. data/spec/lutaml/rdf/mapping_spec.rb +164 -0
  57. data/spec/lutaml/rdf/member_rule_spec.rb +17 -0
  58. data/spec/lutaml/rdf/namespace_set_spec.rb +115 -0
  59. data/spec/lutaml/rdf/namespace_spec.rb +241 -0
  60. data/spec/lutaml/rdf/rdf_transform_spec.rb +82 -0
  61. data/spec/lutaml/turtle/adapter_spec.rb +47 -0
  62. data/spec/lutaml/turtle/mapping_spec.rb +123 -0
  63. data/spec/lutaml/turtle/transform_spec.rb +273 -0
  64. metadata +50 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c512d678682ea756cb7eb7a3d439a7d511b763405936e598900f42f7f07aa705
4
- data.tar.gz: e6623585797f809090619a9e6268e026773d962cac0f940ccae608627d8e714c
3
+ metadata.gz: 2bc036758cf2a4ac1420e14fc6be130730410ab183cd002ced260bdbf08793eb
4
+ data.tar.gz: 1dfc1b473558992eb1481a535d86c1688313951530762c9317af90413028032e
5
5
  SHA512:
6
- metadata.gz: 81558e237a6a7f55c3defee45c8fa8a1d59bf75a875159708065ce8db44308fdaa17d3c727d6ff1074477391ec199008088221f54d5bbb7f2b3b4546ff4d25cf
7
- data.tar.gz: 242730ac59628f328fe07409cc54d7798e75b40d29b6b47ad7382de37a033d9f907d0578d8ab950c45c9ff1b06cd67f6cb98e3e6215948149e63e8e4591be15d
6
+ metadata.gz: 8a4cec37c9acf840b89f6f363aecb8e7bf1a2237d962c6fa8bbe4d4422848cc63812efbd86369780c0e7d26470ff9f220284d062af86b9ebf7f621f3f8e579cb
7
+ data.tar.gz: a48c77e4d99c97fb8cc6fa6793a45b2ba12b79196d9340a23bf4d43a1090c84c05a2402a84ab52792b105713c78caa51f58cfcb141cf79459b06c929adac11b5
@@ -19,6 +19,8 @@ on:
19
19
 
20
20
  jobs:
21
21
  rake:
22
- uses: metanorma/ci/.github/workflows/dependent-rake.yml@main
22
+ uses: metanorma/ci/.github/workflows/dependent-rake.yml@fix/add-pat-token
23
23
  with:
24
24
  command: bundle exec rspec
25
+ secrets:
26
+ pat_token: ${{ secrets.LUTAML_CI_PAT_TOKEN }}
data/.rubocop.yml CHANGED
@@ -24,3 +24,21 @@ Style/OneClassPerFile:
24
24
  - Exclude
25
25
  Exclude:
26
26
  - 'spec/**/*'
27
+
28
+ RSpec/EmptyExampleGroup:
29
+ inherit_mode:
30
+ merge:
31
+ - Exclude
32
+ Exclude:
33
+ - 'spec/lutaml/jsonld/transform_spec.rb'
34
+
35
+ RSpec/NamedSubject:
36
+ inherit_mode:
37
+ merge:
38
+ - Exclude
39
+ Exclude:
40
+ - 'spec/lutaml/turtle/transform_spec.rb'
41
+
42
+ RSpec/ContextMethod:
43
+ Exclude:
44
+ - 'spec/lutaml/jsonld/transform_spec.rb'
data/.rubocop_todo.yml CHANGED
@@ -1,6 +1,6 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config`
3
- # on 2026-05-06 04:21:39 UTC using RuboCop version 1.86.0.
3
+ # on 2026-05-06 13:11:11 UTC using RuboCop version 1.86.1.
4
4
  # The point is for the user to remove these configuration records
5
5
  # one by one as the offenses are removed from the code base.
6
6
  # Note that changes in the inspected code, or installation of new
@@ -11,7 +11,7 @@ Gemspec/RequiredRubyVersion:
11
11
  Exclude:
12
12
  - 'lutaml-model.gemspec'
13
13
 
14
- # Offense count: 3232
14
+ # Offense count: 3264
15
15
  # This cop supports safe autocorrection (--autocorrect).
16
16
  # Configuration parameters: Max, AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, AllowRBSInlineAnnotation, AllowCopDirectives, AllowedPatterns, SplitStrings.
17
17
  # URISchemes: http, https
@@ -34,16 +34,10 @@ Lint/ConstantDefinitionInBlock:
34
34
  Lint/DuplicateBranch:
35
35
  Enabled: false
36
36
 
37
- # Offense count: 22
37
+ # Offense count: 3
38
38
  Lint/DuplicateMethods:
39
39
  Exclude:
40
40
  - 'lib/lutaml/xml/mapping.rb'
41
- - 'spec/lutaml/model/liquid_compatibility_spec.rb'
42
- - 'spec/lutaml/xml/namespace_no_hoisting_spec.rb'
43
- - 'spec/lutaml/xml/namespace_scope_declare_spec.rb'
44
- - 'spec/lutaml/xml/namespace_scope_vcard_spec.rb'
45
- - 'spec/lutaml/xml/prefix_control_spec.rb'
46
- - 'spec/lutaml/xml/type_namespace_examples_spec.rb'
47
41
 
48
42
  # Offense count: 1
49
43
  # This cop supports safe autocorrection (--autocorrect).
@@ -116,7 +110,7 @@ Lint/UselessConstantScoping:
116
110
  - 'lib/lutaml/xml/adapter/nokogiri_adapter.rb'
117
111
  - 'lib/lutaml/xml/mapping_rule.rb'
118
112
 
119
- # Offense count: 346
113
+ # Offense count: 355
120
114
  # Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes, Max.
121
115
  Metrics/AbcSize:
122
116
  Enabled: false
@@ -132,23 +126,23 @@ Metrics/BlockLength:
132
126
  Metrics/BlockNesting:
133
127
  Max: 6
134
128
 
135
- # Offense count: 309
129
+ # Offense count: 315
136
130
  # Configuration parameters: AllowedMethods, AllowedPatterns, Max.
137
131
  Metrics/CyclomaticComplexity:
138
132
  Enabled: false
139
133
 
140
- # Offense count: 562
134
+ # Offense count: 575
141
135
  # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
142
136
  Metrics/MethodLength:
143
137
  Max: 514
144
138
 
145
- # Offense count: 86
139
+ # Offense count: 88
146
140
  # Configuration parameters: CountKeywordArgs.
147
141
  Metrics/ParameterLists:
148
142
  Max: 24
149
143
  MaxOptionalParameters: 5
150
144
 
151
- # Offense count: 260
145
+ # Offense count: 264
152
146
  # Configuration parameters: AllowedMethods, AllowedPatterns, Max.
153
147
  Metrics/PerceivedComplexity:
154
148
  Enabled: false
@@ -240,7 +234,7 @@ RSpec/BeforeAfterAll:
240
234
  RSpec/ContextWording:
241
235
  Enabled: false
242
236
 
243
- # Offense count: 88
237
+ # Offense count: 92
244
238
  # Configuration parameters: IgnoredMetadata.
245
239
  RSpec/DescribeClass:
246
240
  Enabled: false
@@ -251,7 +245,7 @@ RSpec/DescribeMethod:
251
245
  - 'spec/lutaml/xml/schema/xsd/schema_helper_methods_spec.rb'
252
246
  - 'spec/lutaml/xml/serializable_namespace_spec.rb'
253
247
 
254
- # Offense count: 1207
248
+ # Offense count: 1234
255
249
  # Configuration parameters: CountAsOne.
256
250
  RSpec/ExampleLength:
257
251
  Max: 68
@@ -326,7 +320,7 @@ RSpec/MultipleDescribes:
326
320
  - 'spec/lutaml/xml/namespace_resolution_strategy_spec.rb'
327
321
  - 'spec/lutaml/xml/xml_space_type_spec.rb'
328
322
 
329
- # Offense count: 1383
323
+ # Offense count: 1442
330
324
  RSpec/MultipleExpectations:
331
325
  Max: 21
332
326
 
@@ -374,7 +368,7 @@ RSpec/RepeatedExampleGroupDescription:
374
368
  Exclude:
375
369
  - 'spec/lutaml/model/mixed_content_spec.rb'
376
370
 
377
- # Offense count: 35
371
+ # Offense count: 40
378
372
  # Configuration parameters: CustomTransform, IgnoreMethods, IgnoreMetadata, InflectorPath, EnforcedInflector.
379
373
  # SupportedInflectors: default, active_support
380
374
  RSpec/SpecFilePathFormat:
data/Gemfile CHANGED
@@ -12,6 +12,7 @@ gem "base64"
12
12
  gem "benchmark-ips"
13
13
  gem "bigdecimal"
14
14
  gem "canon" # , path: "../canon"
15
+ gem "json-ld", "~> 3.3"
15
16
  gem "liquid", "~> 5"
16
17
  gem "multi_json"
17
18
  gem "nokogiri"
@@ -20,6 +21,7 @@ gem "oj"
20
21
  gem "openssl", "~> 3.0"
21
22
  gem "ox"
22
23
  gem "rake"
24
+ gem "rdf-turtle", "~> 3.3"
23
25
  gem "rexml"
24
26
  gem "rspec"
25
27
  gem "rubocop"
data/README.adoc CHANGED
@@ -27,7 +27,7 @@ for:
27
27
 
28
28
  It provides simple, flexible and comprehensive mechanisms for defining
29
29
  information models with attributes and types, and the serialization of them
30
- to/from serialization formats including JSON, XML, YAML, and TOML, as well as
30
+ to/from serialization formats including JSON, XML, YAML, TOML, JSON-LD, and Turtle, as well as
31
31
  transformation to other formats like Hash.
32
32
 
33
33
  For serialization formats, it uses an adapter pattern to support multiple
@@ -48,7 +48,7 @@ link:docs/migration-guides/0-1-0-migrate-from-shale.adoc[Migrating from Shale to
48
48
  == Features
49
49
 
50
50
  * Define models with attributes and types
51
- * Serialize and deserialize models to/from JSON, XML, YAML, and TOML
51
+ * Serialize and deserialize models to/from JSON, XML, YAML, TOML, JSON-LD, and Turtle
52
52
  * Transform models to other formats like Hash
53
53
  * Support for multiple serialization libraries (e.g., `toml-rb`, `tomlib`)
54
54
  * Configurable adapters for different serialization formats
@@ -65,6 +65,7 @@ link:docs/migration-guides/0-1-0-migrate-from-shale.adoc[Migrating from Shale to
65
65
  * Symmetric OOP architecture for all formats with KeyValueDataModel (see <<keyvaluedatamodel-architecture>>)
66
66
  * Parse XSD schemas into Ruby objects for inspection and manipulation (see <<xsd-schema-parsing>>)
67
67
  * Reusable XML mapping classes for sharing mappings across models
68
+ * RDF namespace classes and JSON-LD/Turtle format adapters for Linked Data serialization
68
69
  * Consolidation mapping: group sibling XML elements into structured model instances (see <<consolidation-mapping>>)
69
70
  * Document-level validation framework with composable rules, profiles, and remediation (see <<document-validation-framework>>)
70
71
 
@@ -5391,6 +5392,11 @@ Collection serialization formats::
5391
5392
  `jsonl`::: JSONL (JSON Lines) (see <<mapping-collections>>)
5392
5393
  `yamls`::: YAML Stream (multi-document format) (see <<mapping-collections>>)
5393
5394
 
5395
+ RDF serialization formats::
5396
+
5397
+ `rdf`::: Unified RDF mapping for both JSON-LD and Turtle (see <<mapping-rdf>>)
5398
+ `turtle`::: Turtle (see link:docs/_guides/turtle-serialization.adoc[Turtle Serialization])
5399
+
5394
5400
 
5395
5401
  .Using the `xml`, `hsh`, `json`, `yaml`, `toml` and `key_value` blocks to define serialization mappings
5396
5402
  [example]
@@ -5439,6 +5445,73 @@ end
5439
5445
  ====
5440
5446
 
5441
5447
 
5448
+ [[mapping-rdf]]
5449
+ === Unified RDF mapping (`rdf`)
5450
+
5451
+ The `rdf` block defines predicate-based mappings once for both JSON-LD and
5452
+ Turtle serialization. It follows the same unified-mapping principle as
5453
+ `key_value` (which serves JSON, YAML, TOML).
5454
+
5455
+ .Using the `rdf` block to define unified RDF mappings
5456
+ [example]
5457
+ ====
5458
+ [source,ruby]
5459
+ ----
5460
+ class Concept < Lutaml::Model::Serializable
5461
+ attribute :code, :string
5462
+ attribute :name, :string
5463
+
5464
+ rdf do
5465
+ namespace SkosNamespace
5466
+
5467
+ subject { |m| "http://example.org/concept/#{m.code}" }
5468
+ type "skos:Concept"
5469
+
5470
+ predicate :notation, namespace: SkosNamespace, to: :code
5471
+ predicate :prefLabel, namespace: SkosNamespace, to: :name
5472
+ end
5473
+ end
5474
+ ----
5475
+
5476
+ Serializes to both formats:
5477
+
5478
+ [source,ruby]
5479
+ ----
5480
+ concept = Concept.new(code: "2119", name: "component")
5481
+ concept.to_turtle # => Turtle with @prefix, a skos:Concept, predicates
5482
+ concept.to_jsonld # => JSON-LD with @context, @type, @id, properties
5483
+ ----
5484
+ ====
5485
+
5486
+ Graph-level serialization uses `members` to emit all contained resources as
5487
+ separate subjects in the same document:
5488
+
5489
+ [source,ruby]
5490
+ ----
5491
+ class Vocabulary < Lutaml::Model::Serializable
5492
+ attribute :id, :string
5493
+ attribute :concepts, Concept, collection: true
5494
+
5495
+ rdf do
5496
+ namespace SkosNamespace
5497
+ subject { |v| "http://example.org/vocab/#{v.id}" }
5498
+ type "skos:ConceptScheme"
5499
+ predicate :prefLabel, namespace: SkosNamespace, to: :id
5500
+ members :concepts
5501
+ end
5502
+ end
5503
+
5504
+ # Turtle: single document with ConceptScheme + all Concept triples
5505
+ # JSON-LD: @graph array with ConceptScheme + all Concept objects
5506
+ vocab.to_turtle
5507
+ vocab.to_jsonld
5508
+ ----
5509
+
5510
+ See link:docs/_guides/rdf-serialization.adoc[Unified RDF Serialization] for the
5511
+ complete guide including language-tagged values, graph serialization, and
5512
+ architecture details.
5513
+
5514
+
5442
5515
  == Serialization: XML
5443
5516
 
5444
5517
  === General
@@ -10100,8 +10173,14 @@ Key-value data models supported are identified by their short name:
10100
10173
  `json`:: JSON
10101
10174
  `yaml`:: YAML
10102
10175
  `toml`:: TOML
10176
+ `jsonld`:: JSON-LD (extends key-value with `@context`, `@type`, `@id`)
10103
10177
  `key_value`:: A way to configure key-value mappings for all supported key-value data models.
10104
10178
 
10179
+ RDF serialization formats::
10180
+
10181
+ `rdf`:: Unified RDF mapping for both JSON-LD and Turtle (see <<mapping-rdf>>)
10182
+ `turtle`:: Turtle (see link:docs/_guides/turtle-serialization.adoc[Turtle Serialization])
10183
+
10105
10184
 
10106
10185
  === Mapping
10107
10186
 
@@ -15735,6 +15814,8 @@ LutaML, out of the box, supports the following serialization formats:
15735
15814
  * YAML (https://yaml.org/[YAML version 1.2])
15736
15815
  * JSON (https://www.ecma-international.org/publications-and-standards/standards/ecma-404/[ECMA-404 The JSON Data Interchange Standard], unofficial link: https://www.json.org[JSON])
15737
15816
  * TOML (https://toml.io/en[TOML version 1.0])
15817
+ * JSON-LD (https://www.w3.org/TR/json-ld11/[W3C JSON-LD 1.1])
15818
+ * Turtle (https://www.w3.org/TR/turtle/[W3C RDF 1.1 Turtle])
15738
15819
 
15739
15820
  The adapter interface is also used to support certain transformation of models
15740
15821
  into an "end format", which is not a serialization format. For example, the
@@ -15977,6 +16058,37 @@ end
15977
16058
  ----
15978
16059
 
15979
16060
 
16061
+ === JSON-LD
16062
+
16063
+ Lutaml::Model supports serialization to and from JSON-LD (W3C JSON-LD 1.1).
16064
+ JSON-LD is a key-value format that extends JSON with `@context`, `@type`, and
16065
+ `@id`. No additional gem is required beyond `lutaml-model`.
16066
+
16067
+ For unified RDF mapping (both JSON-LD and Turtle from a single `rdf` block), see
16068
+ link:docs/_guides/rdf-serialization.adoc[Unified RDF Serialization].
16069
+
16070
+ See link:docs/_guides/jsonld-serialization.adoc[JSON-LD Serialization] for the
16071
+ legacy `jsonld do` block DSL and usage examples.
16072
+
16073
+
16074
+ === Turtle
16075
+
16076
+ Lutaml::Model supports serialization to and from W3C RDF Turtle format.
16077
+ Turtle is a non-key-value format based on RDF triples. Requires the
16078
+ `rdf-turtle` gem:
16079
+
16080
+ [source,ruby]
16081
+ ----
16082
+ gem "rdf-turtle", "~> 3.3"
16083
+ ----
16084
+
16085
+ For unified RDF mapping (both JSON-LD and Turtle from a single `rdf` block), see
16086
+ link:docs/_guides/rdf-serialization.adoc[Unified RDF Serialization].
16087
+
16088
+ See link:docs/_guides/turtle-serialization.adoc[Turtle Serialization] for the
16089
+ legacy `turtle do` block DSL and usage examples.
16090
+
16091
+
15980
16092
  === Per-operation adapter override
15981
16093
 
15982
16094
  Override the adapter for a single serialization/deserialization call using the `adapter:` option:
@@ -22,6 +22,17 @@ Task-oriented guides for accomplishing specific goals with Lutaml::Model.
22
22
  * link:../keyvalue-serialization[Key-Value Serialization] - JSON/YAML/TOML/Hash
23
23
  * link:../collection-serialization[Collection Serialization] - JSONL and YAML Stream
24
24
 
25
+ == Linked Data serialization
26
+
27
+ * link:../rdf-serialization[Unified RDF Serialization] - One `rdf` block for both JSON-LD and Turtle
28
+ * link:../jsonld-serialization[JSON-LD Serialization] - W3C JSON-LD 1.1 with `@context`
29
+ * link:../turtle-serialization[Turtle Serialization] - W3C RDF Turtle format
30
+
31
+ == RDF infrastructure
32
+
33
+ See link:../references/rdf-namespaces[RDF Namespaces] for namespace classes,
34
+ `NamespaceSet`, `Iri`, and `Literal` value objects.
35
+
25
36
  == Advanced features
26
37
 
27
38
  * link:../value-transformations[Value Transformations] - Transform values during serialization
@@ -49,6 +60,13 @@ Task-oriented guides for accomplishing specific goals with Lutaml::Model.
49
60
  . link:../value-transformations[Transform values if needed]
50
61
  . link:../missing-values-handling[Handle nil/empty values]
51
62
 
63
+ === I want to serialize to JSON-LD or Turtle (Linked Data)
64
+
65
+ . link:../rdf-serialization[Unified RDF Serialization] (recommended — one block for both formats)
66
+ . link:../jsonld-serialization[JSON-LD Serialization guide] (legacy `jsonld do` block)
67
+ . link:../turtle-serialization[Turtle Serialization guide] (legacy `turtle do` block)
68
+ . link:../references/rdf-namespaces[RDF Namespaces reference]
69
+
52
70
  === I want to work with collections
53
71
 
54
72
  . link:../collection-serialization[Collection Serialization]
@@ -0,0 +1,217 @@
1
+ ---
2
+ title: JSON-LD Serialization
3
+ nav_order: 15
4
+ ---
5
+
6
+ = JSON-LD Serialization
7
+
8
+ Lutaml::Model supports serialization to and from JSON-LD (W3C JSON-LD 1.1) using
9
+ SKOS, Dublin Core Terms, and other W3C vocabularies.
10
+
11
+ == Setup
12
+
13
+ JSON-LD is built on the key-value serialization pipeline and requires no
14
+ additional gems beyond what JSON already uses. The `json-ld` gem may be added
15
+ for advanced JSON-LD Processing (expansion, compaction, flattening) in the
16
+ future.
17
+
18
+ JSON-LD is a **key-value** format adapter that extends the built-in key-value
19
+ serialization with JSON-LD-specific constructs: `@context`, `@type`, and
20
+ `@id`.
21
+
22
+ == Mapping DSL
23
+
24
+ Use the `jsonld do` block in your model to define JSON-LD mappings:
25
+
26
+ [source,ruby]
27
+ ----
28
+ class Concept < Lutaml::Model::Serializable
29
+ attribute :name, :string
30
+ attribute :description, :string
31
+
32
+ jsonld do
33
+ context do
34
+ prefix Lutaml::Rdf::Namespaces::SkosNamespace
35
+ vocab "http://example.org/ns/"
36
+
37
+ term "name", id: "http://example.org/name"
38
+ term "description", id: "http://example.org/description"
39
+ end
40
+
41
+ type "skos:Concept"
42
+ id { |m| "http://example.org/concept/#{m.name}" }
43
+
44
+ map "name", to: :name
45
+ map "description", to: :description
46
+ end
47
+ end
48
+ ----
49
+
50
+ === Context
51
+
52
+ The `context` block builds the `@context` object in the JSON-LD output:
53
+
54
+ * `prefix(NamespaceClass)` — registers an RDF namespace as a prefix
55
+ * `vocab(uri)` — sets `@vocab` for unprefixed terms
56
+ * `language(code)` — sets default `@language`
57
+ * `base(uri)` — sets `@base` URI
58
+ * `term(name, ...)` — defines a term with optional `id:`, `type:`,
59
+ `container:`, `language:`, `reverse:`
60
+
61
+ Context resolution:
62
+
63
+ * Compact IRIs (e.g., `"skos:Concept"`) are expanded to full URIs via prefix
64
+ * Terms are resolved via the term definitions
65
+ * Unprefixed names are resolved via `@vocab`
66
+
67
+ === Type and ID
68
+
69
+ * `type "skos:Concept"` — sets `@type` in the output (resolved to full IRI via
70
+ context)
71
+ * `id { |model| ... }` — generates `@id` from the model instance
72
+
73
+ Both are optional. Omit `type` or `id` if your JSON-LD document does not
74
+ require them.
75
+
76
+ === map
77
+
78
+ `map` entries work identically to key-value serialization, defining the JSON
79
+ properties serialized from model attributes.
80
+
81
+ == Serialization
82
+
83
+ [source,ruby]
84
+ ----
85
+ concept = Concept.new(name: "test", description: "A test concept")
86
+ jsonld = concept.to_jsonld
87
+ ----
88
+
89
+ Produces:
90
+
91
+ [source,json]
92
+ ----
93
+ {
94
+ "@context": {
95
+ "skos": "http://www.w3.org/2004/02/skos/core#",
96
+ "@vocab": "http://example.org/ns/",
97
+ "name": "http://example.org/name",
98
+ "description": "http://example.org/description"
99
+ },
100
+ "@type": "http://www.w3.org/2004/02/skos/core#Concept",
101
+ "@id": "http://example.org/concept/test",
102
+ "name": "test",
103
+ "description": "A test concept"
104
+ }
105
+ ----
106
+
107
+ Nil attribute values are omitted from the output.
108
+
109
+ == Deserialization
110
+
111
+ [source,ruby]
112
+ ----
113
+ concept = Concept.from_jsonld(jsonld_string)
114
+ puts concept.name # => "test"
115
+ ----
116
+
117
+ The `from_jsonld` method:
118
+
119
+ . Parses the JSON-LD string into a hash via `JSON.parse`
120
+ . Strips all `@`-prefixed keywords (`@context`, `@type`, `@id`, `@graph`,
121
+ etc.) before attribute mapping
122
+ . Delegates attribute mapping to the key-value transform pipeline
123
+
124
+ This prevents JSON-LD keywords from colliding with model attributes named
125
+ `type`, `id`, `context`, etc.
126
+
127
+ == Round-trip
128
+
129
+ Model data round-trips through JSON-LD serialization:
130
+
131
+ [source,ruby]
132
+ ----
133
+ restored = Concept.from_jsonld(concept.to_jsonld)
134
+ restored.name == concept.name # => true
135
+ ----
136
+
137
+ The `@context` structure is preserved across round-trips because it is defined
138
+ in the mapping, not derived from the input.
139
+
140
+ == Unified `rdf` DSL
141
+
142
+ If you need both JSON-LD and Turtle output from the same model, use the
143
+ unified `rdf` DSL instead of separate `jsonld` and `turtle` blocks. This
144
+ defines the mapping once and auto-generates `@context` from predicates:
145
+
146
+ [source,ruby]
147
+ ----
148
+ class Concept < Lutaml::Model::Serializable
149
+ attribute :name, :string
150
+ attribute :code, :string
151
+
152
+ rdf do
153
+ namespace Lutaml::Rdf::Namespaces::SkosNamespace
154
+ subject { |m| "http://example.org/#{m.code}" }
155
+ type "skos:Concept"
156
+ predicate :prefLabel, namespace: SkosNamespace, to: :name
157
+ predicate :notation, namespace: SkosNamespace, to: :code
158
+ end
159
+ end
160
+ ----
161
+
162
+ See link:../_guides/rdf-serialization.adoc[Unified RDF Serialization] for the
163
+ complete guide including graph-level serialization with `members`.
164
+
165
+ == Multi-format models
166
+
167
+ A single model can define `json`, `jsonld`, `turtle`, and `rdf` mappings
168
+ simultaneously. Each format operates independently:
169
+
170
+ [source,ruby]
171
+ ----
172
+ class Concept < Lutaml::Model::Serializable
173
+ attribute :name, :string
174
+ attribute :code, :string
175
+
176
+ json do
177
+ map "name", to: :name
178
+ map "code", to: :code
179
+ end
180
+
181
+ rdf do
182
+ namespace Lutaml::Rdf::Namespaces::SkosNamespace
183
+ subject { |m| "http://example.org/#{m.code}" }
184
+ type "skos:Concept"
185
+ predicate :prefLabel, namespace: SkosNamespace, to: :name
186
+ predicate :notation, namespace: SkosNamespace, to: :code
187
+ end
188
+ end
189
+ ----
190
+
191
+ * `to_json` produces plain JSON without `@context`
192
+ * `to_jsonld` produces JSON-LD with auto-generated `@context`, `@type`, `@id`
193
+ * `to_turtle` produces Turtle with `@prefix` declarations
194
+
195
+ The unified `rdf` block creates mappings for both `:turtle` and `:jsonld`
196
+ formats. If separate `jsonld do` or `turtle do` blocks are also defined, they
197
+ take precedence over the `rdf` block for their respective format.
198
+
199
+ == Error Handling
200
+
201
+ * `JSON::ParserError` — raised for malformed JSON input
202
+ * All parsing errors are wrapped in `Lutaml::Model::InvalidFormatError` by the
203
+ format pipeline
204
+
205
+ == Architecture
206
+
207
+ The JSON-LD format is composed of:
208
+
209
+ * `Lutaml::JsonLd::Adapter` — extends `KeyValue::Document`; parses JSON-LD
210
+ strings to hashes and serializes hashes back to JSON
211
+ * `Lutaml::JsonLd::Context` — DSL for building `@context` with prefixes,
212
+ vocab, terms, language, and base
213
+ * `Lutaml::JsonLd::TermDefinition` — value object for term definitions with
214
+ `@id`, `@type`, `@container`, `@language`, `@reverse`
215
+ * `Lutaml::JsonLd::Transform` — inherits from `Rdf::Transform`; auto-generates
216
+ `@context` from predicates, injects `@type`/`@id` on export, strips
217
+ `@`-keywords on import