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.
- checksums.yaml +4 -4
- data/.github/workflows/dependent-tests.yml +3 -1
- data/.rubocop.yml +18 -0
- data/.rubocop_todo.yml +12 -18
- data/Gemfile +2 -0
- data/README.adoc +114 -2
- data/docs/_guides/index.adoc +18 -0
- data/docs/_guides/jsonld-serialization.adoc +217 -0
- data/docs/_guides/rdf-serialization.adoc +344 -0
- data/docs/_guides/turtle-serialization.adoc +224 -0
- data/docs/_pages/serialization_adapters.adoc +31 -0
- data/docs/_references/index.adoc +1 -0
- data/docs/_references/rdf-namespaces.adoc +243 -0
- data/docs/index.adoc +3 -2
- data/lib/lutaml/jsonld/adapter.rb +23 -0
- data/lib/lutaml/jsonld/context.rb +69 -0
- data/lib/lutaml/jsonld/term_definition.rb +39 -0
- data/lib/lutaml/jsonld/transform.rb +174 -0
- data/lib/lutaml/jsonld.rb +23 -0
- data/lib/lutaml/model/format_registry.rb +10 -1
- data/lib/lutaml/model/serialize/format_conversion.rb +17 -1
- data/lib/lutaml/model/version.rb +1 -1
- data/lib/lutaml/model.rb +6 -0
- data/lib/lutaml/rdf/error.rb +7 -0
- data/lib/lutaml/rdf/iri.rb +44 -0
- data/lib/lutaml/rdf/language_tagged.rb +11 -0
- data/lib/lutaml/rdf/literal.rb +62 -0
- data/lib/lutaml/rdf/mapping.rb +71 -0
- data/lib/lutaml/rdf/mapping_rule.rb +35 -0
- data/lib/lutaml/rdf/member_rule.rb +13 -0
- data/lib/lutaml/rdf/namespace.rb +58 -0
- data/lib/lutaml/rdf/namespace_set.rb +69 -0
- data/lib/lutaml/rdf/namespaces/dcterms_namespace.rb +12 -0
- data/lib/lutaml/rdf/namespaces/owl_namespace.rb +12 -0
- data/lib/lutaml/rdf/namespaces/rdf_namespace.rb +14 -0
- data/lib/lutaml/rdf/namespaces/rdfs_namespace.rb +12 -0
- data/lib/lutaml/rdf/namespaces/skos_namespace.rb +12 -0
- data/lib/lutaml/rdf/namespaces/xsd_namespace.rb +12 -0
- data/lib/lutaml/rdf/namespaces.rb +14 -0
- data/lib/lutaml/rdf/transform.rb +36 -0
- data/lib/lutaml/rdf.rb +19 -0
- data/lib/lutaml/turtle/adapter.rb +35 -0
- data/lib/lutaml/turtle/mapping.rb +7 -0
- data/lib/lutaml/turtle/transform.rb +158 -0
- data/lib/lutaml/turtle.rb +22 -0
- data/spec/lutaml/integration/edge_cases_spec.rb +109 -0
- data/spec/lutaml/integration/multi_format_spec.rb +106 -0
- data/spec/lutaml/integration/round_trip_spec.rb +170 -0
- data/spec/lutaml/jsonld/adapter_spec.rb +46 -0
- data/spec/lutaml/jsonld/context_spec.rb +114 -0
- data/spec/lutaml/jsonld/term_definition_spec.rb +55 -0
- data/spec/lutaml/jsonld/transform_spec.rb +211 -0
- data/spec/lutaml/rdf/graph_serialization_spec.rb +137 -0
- data/spec/lutaml/rdf/iri_spec.rb +73 -0
- data/spec/lutaml/rdf/literal_spec.rb +98 -0
- data/spec/lutaml/rdf/mapping_spec.rb +164 -0
- data/spec/lutaml/rdf/member_rule_spec.rb +17 -0
- data/spec/lutaml/rdf/namespace_set_spec.rb +115 -0
- data/spec/lutaml/rdf/namespace_spec.rb +241 -0
- data/spec/lutaml/rdf/rdf_transform_spec.rb +82 -0
- data/spec/lutaml/turtle/adapter_spec.rb +47 -0
- data/spec/lutaml/turtle/mapping_spec.rb +123 -0
- data/spec/lutaml/turtle/transform_spec.rb +273 -0
- metadata +50 -1
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Unified RDF Serialization
|
|
3
|
+
nav_order: 17
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
= Unified RDF Serialization
|
|
7
|
+
|
|
8
|
+
Lutaml::Model provides a unified `rdf` DSL that defines RDF mappings once for
|
|
9
|
+
both JSON-LD and Turtle serialization. This follows the same principle as
|
|
10
|
+
`key_value do ... end` which serves JSON, YAML, and TOML from a single block.
|
|
11
|
+
|
|
12
|
+
Both JSON-LD and Turtle are RDF serialization formats representing the same
|
|
13
|
+
subject–predicate–object triples. The `rdf` DSL lets you define the mapping
|
|
14
|
+
once, and the format adapters handle syntax differences automatically.
|
|
15
|
+
|
|
16
|
+
== Setup
|
|
17
|
+
|
|
18
|
+
Add the `rdf-turtle` gem to your Gemfile (required for Turtle output):
|
|
19
|
+
|
|
20
|
+
[source,ruby]
|
|
21
|
+
----
|
|
22
|
+
gem "rdf-turtle", "~> 3.3"
|
|
23
|
+
----
|
|
24
|
+
|
|
25
|
+
No additional gem is needed for JSON-LD output.
|
|
26
|
+
|
|
27
|
+
== When to Use `rdf` vs Separate `jsonld`/`turtle` Blocks
|
|
28
|
+
|
|
29
|
+
Use `rdf do ... end` when:
|
|
30
|
+
* Your model maps to RDF resources using standard vocabularies (SKOS, DC, etc.)
|
|
31
|
+
* You need both JSON-LD and Turtle output from the same model
|
|
32
|
+
* You want predicate-based mapping with automatic `@context` generation
|
|
33
|
+
|
|
34
|
+
Use `jsonld do ... end` when:
|
|
35
|
+
* You need full control over the JSON-LD `@context` structure
|
|
36
|
+
* You are mapping to JSON properties that don't map to RDF predicates
|
|
37
|
+
|
|
38
|
+
Use `turtle do ... end` when:
|
|
39
|
+
* You only need Turtle output
|
|
40
|
+
* You need Turtle-specific features not available in the unified DSL
|
|
41
|
+
|
|
42
|
+
Both the unified `rdf` block and the format-specific blocks can coexist on the
|
|
43
|
+
same model. If both are defined, format-specific blocks take precedence.
|
|
44
|
+
|
|
45
|
+
== The `rdf` DSL
|
|
46
|
+
|
|
47
|
+
=== Basic Model
|
|
48
|
+
|
|
49
|
+
[source,ruby]
|
|
50
|
+
----
|
|
51
|
+
class Concept < Lutaml::Model::Serializable
|
|
52
|
+
attribute :code, :string
|
|
53
|
+
attribute :name, :string
|
|
54
|
+
|
|
55
|
+
rdf do
|
|
56
|
+
namespace SkosNamespace
|
|
57
|
+
|
|
58
|
+
subject { |m| "http://example.org/concept/#{m.code}" }
|
|
59
|
+
type "skos:Concept"
|
|
60
|
+
|
|
61
|
+
predicate :notation, namespace: SkosNamespace, to: :code
|
|
62
|
+
predicate :prefLabel, namespace: SkosNamespace, to: :name
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
----
|
|
66
|
+
|
|
67
|
+
This single `rdf` block creates mappings for both `:turtle` and `:jsonld`
|
|
68
|
+
formats automatically.
|
|
69
|
+
|
|
70
|
+
=== DSL Methods
|
|
71
|
+
|
|
72
|
+
==== namespace
|
|
73
|
+
|
|
74
|
+
Declares the RDF namespaces used by predicates. Accepts one or more
|
|
75
|
+
`Lutaml::Rdf::Namespace` subclass references:
|
|
76
|
+
|
|
77
|
+
[source,ruby]
|
|
78
|
+
----
|
|
79
|
+
namespace SkosNamespace, DctermsNamespace
|
|
80
|
+
----
|
|
81
|
+
|
|
82
|
+
In Turtle output, each namespace becomes an `@prefix` declaration. In JSON-LD
|
|
83
|
+
output, each becomes a prefix entry in `@context`.
|
|
84
|
+
|
|
85
|
+
==== subject
|
|
86
|
+
|
|
87
|
+
Required for top-level resources. A block that generates the subject URI from
|
|
88
|
+
the model instance:
|
|
89
|
+
|
|
90
|
+
[source,ruby]
|
|
91
|
+
----
|
|
92
|
+
subject { |m| "http://example.org/concept/#{m.code}" }
|
|
93
|
+
----
|
|
94
|
+
|
|
95
|
+
==== type
|
|
96
|
+
|
|
97
|
+
Sets the RDF type (`rdf:type`). Compact IRIs are resolved via declared
|
|
98
|
+
namespaces:
|
|
99
|
+
|
|
100
|
+
[source,ruby]
|
|
101
|
+
----
|
|
102
|
+
type "skos:Concept"
|
|
103
|
+
# resolves to <http://www.w3.org/2004/02/skos/core#Concept>
|
|
104
|
+
----
|
|
105
|
+
|
|
106
|
+
==== predicate
|
|
107
|
+
|
|
108
|
+
Each `predicate` creates a mapping between an RDF predicate and a model
|
|
109
|
+
attribute:
|
|
110
|
+
|
|
111
|
+
[source,ruby]
|
|
112
|
+
----
|
|
113
|
+
predicate :prefLabel, namespace: SkosNamespace, to: :name, lang_tagged: true
|
|
114
|
+
----
|
|
115
|
+
|
|
116
|
+
Parameters:
|
|
117
|
+
|
|
118
|
+
* `name` — the local name in the namespace (e.g., `:prefLabel`)
|
|
119
|
+
* `namespace:` — the `Lutaml::Rdf::Namespace` subclass (required)
|
|
120
|
+
* `to:` — the model attribute to read (required)
|
|
121
|
+
* `lang_tagged:` (default: `false`) — if true, values are serialized with
|
|
122
|
+
language tags (see <<language-tagged-values>>)
|
|
123
|
+
|
|
124
|
+
==== members
|
|
125
|
+
|
|
126
|
+
Declares that a container model contains member resources that should be
|
|
127
|
+
serialized as separate subjects in the output graph (see <<graph-serialization>>).
|
|
128
|
+
|
|
129
|
+
== Serialization
|
|
130
|
+
|
|
131
|
+
=== Turtle
|
|
132
|
+
|
|
133
|
+
[source,ruby]
|
|
134
|
+
----
|
|
135
|
+
concept = Concept.new(code: "2119", name: "component")
|
|
136
|
+
turtle = concept.to_turtle
|
|
137
|
+
----
|
|
138
|
+
|
|
139
|
+
Produces:
|
|
140
|
+
|
|
141
|
+
[source,turtle]
|
|
142
|
+
----
|
|
143
|
+
@prefix skos: <http://www.w3.org/2004/02/skos/core#> .
|
|
144
|
+
|
|
145
|
+
<http://example.org/concept/2119> a skos:Concept;
|
|
146
|
+
skos:notation "2119";
|
|
147
|
+
skos:prefLabel "component" .
|
|
148
|
+
----
|
|
149
|
+
|
|
150
|
+
=== JSON-LD
|
|
151
|
+
|
|
152
|
+
[source,ruby]
|
|
153
|
+
----
|
|
154
|
+
jsonld = concept.to_jsonld
|
|
155
|
+
----
|
|
156
|
+
|
|
157
|
+
Produces:
|
|
158
|
+
|
|
159
|
+
[source,json]
|
|
160
|
+
----
|
|
161
|
+
{
|
|
162
|
+
"@context": {
|
|
163
|
+
"skos": "http://www.w3.org/2004/02/skos/core#",
|
|
164
|
+
"notation": "skos:notation",
|
|
165
|
+
"prefLabel": "skos:prefLabel"
|
|
166
|
+
},
|
|
167
|
+
"@type": "skos:Concept",
|
|
168
|
+
"@id": "http://example.org/concept/2119",
|
|
169
|
+
"notation": "2119",
|
|
170
|
+
"prefLabel": "component"
|
|
171
|
+
}
|
|
172
|
+
----
|
|
173
|
+
|
|
174
|
+
The `@context` is auto-generated from the declared namespaces and predicates.
|
|
175
|
+
No manual context definition is needed.
|
|
176
|
+
|
|
177
|
+
== [[language-tagged-values]]Language-Tagged Values
|
|
178
|
+
|
|
179
|
+
When a predicate is declared with `lang_tagged: true`, values are serialized
|
|
180
|
+
with language information:
|
|
181
|
+
|
|
182
|
+
**Turtle:**
|
|
183
|
+
```turtle
|
|
184
|
+
skos:prefLabel "component"@eng ;
|
|
185
|
+
skos:prefLabel "composant"@fra ;
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
**JSON-LD:**
|
|
189
|
+
```json
|
|
190
|
+
"prefLabel": { "eng": "component", "fra": "composant" }
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
In JSON-LD, the `@context` auto-generates a language container:
|
|
194
|
+
```json
|
|
195
|
+
"prefLabel": { "@id": "skos:prefLabel", "@container": "@language" }
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
For this to work, the attribute's values must include the
|
|
199
|
+
`Lutaml::Rdf::LanguageTagged` module (or be a `Lutaml::Rdf::Literal`). A simple
|
|
200
|
+
value object is sufficient:
|
|
201
|
+
|
|
202
|
+
[source,ruby]
|
|
203
|
+
----
|
|
204
|
+
class LocalizedLiteral < Lutaml::Model::Serializable
|
|
205
|
+
attribute :value, :string
|
|
206
|
+
attribute :language_code, :string
|
|
207
|
+
end
|
|
208
|
+
----
|
|
209
|
+
|
|
210
|
+
== [[graph-serialization]]Graph Serialization
|
|
211
|
+
|
|
212
|
+
When a container model holds a collection of member resources, use `members` to
|
|
213
|
+
serialize them as separate subjects in the same RDF graph.
|
|
214
|
+
|
|
215
|
+
=== Container Model
|
|
216
|
+
|
|
217
|
+
[source,ruby]
|
|
218
|
+
----
|
|
219
|
+
class Vocabulary < Lutaml::Model::Serializable
|
|
220
|
+
attribute :id, :string
|
|
221
|
+
attribute :concepts, Concept, collection: true
|
|
222
|
+
|
|
223
|
+
rdf do
|
|
224
|
+
namespace SkosNamespace
|
|
225
|
+
|
|
226
|
+
subject { |v| "http://example.org/vocab/#{v.id}" }
|
|
227
|
+
type "skos:ConceptScheme"
|
|
228
|
+
predicate :prefLabel, namespace: SkosNamespace, to: :id
|
|
229
|
+
|
|
230
|
+
members :concepts
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
----
|
|
234
|
+
|
|
235
|
+
=== Turtle Output with Members
|
|
236
|
+
|
|
237
|
+
[source,ruby]
|
|
238
|
+
----
|
|
239
|
+
vocab = Vocabulary.new(id: "iso1087", concepts: [concept1, concept2])
|
|
240
|
+
puts vocab.to_turtle
|
|
241
|
+
----
|
|
242
|
+
|
|
243
|
+
Produces a single Turtle document with the container and all member triples:
|
|
244
|
+
|
|
245
|
+
[source,turtle]
|
|
246
|
+
----
|
|
247
|
+
@prefix skos: <http://www.w3.org/2004/02/skos/core#> .
|
|
248
|
+
|
|
249
|
+
<http://example.org/vocab/iso1087> a skos:ConceptScheme;
|
|
250
|
+
skos:prefLabel "iso1087" .
|
|
251
|
+
|
|
252
|
+
<http://example.org/concept/2119> a skos:Concept;
|
|
253
|
+
skos:notation "2119";
|
|
254
|
+
skos:prefLabel "component" .
|
|
255
|
+
|
|
256
|
+
<http://example.org/concept/2120> a skos:Concept;
|
|
257
|
+
skos:notation "2120";
|
|
258
|
+
skos:prefLabel "intension" .
|
|
259
|
+
----
|
|
260
|
+
|
|
261
|
+
=== JSON-LD Output with Members
|
|
262
|
+
|
|
263
|
+
[source,ruby]
|
|
264
|
+
----
|
|
265
|
+
puts vocab.to_jsonld
|
|
266
|
+
----
|
|
267
|
+
|
|
268
|
+
Produces a JSON-LD document with `@graph` containing all resources:
|
|
269
|
+
|
|
270
|
+
[source,json]
|
|
271
|
+
----
|
|
272
|
+
{
|
|
273
|
+
"@context": {
|
|
274
|
+
"skos": "http://www.w3.org/2004/02/skos/core#",
|
|
275
|
+
"prefLabel": "skos:prefLabel",
|
|
276
|
+
"notation": "skos:notation"
|
|
277
|
+
},
|
|
278
|
+
"@graph": [
|
|
279
|
+
{
|
|
280
|
+
"@id": "http://example.org/vocab/iso1087",
|
|
281
|
+
"@type": "skos:ConceptScheme",
|
|
282
|
+
"prefLabel": "iso1087"
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
"@id": "http://example.org/concept/2119",
|
|
286
|
+
"@type": "skos:Concept",
|
|
287
|
+
"notation": "2119",
|
|
288
|
+
"prefLabel": "component"
|
|
289
|
+
},
|
|
290
|
+
{
|
|
291
|
+
"@id": "http://example.org/concept/2120",
|
|
292
|
+
"@type": "skos:Concept",
|
|
293
|
+
"notation": "2120",
|
|
294
|
+
"prefLabel": "intension"
|
|
295
|
+
}
|
|
296
|
+
]
|
|
297
|
+
}
|
|
298
|
+
----
|
|
299
|
+
|
|
300
|
+
=== Member-Only Models
|
|
301
|
+
|
|
302
|
+
A container model without a `subject` block serializes only the member triples.
|
|
303
|
+
This is useful when you don't need a container resource:
|
|
304
|
+
|
|
305
|
+
[source,ruby]
|
|
306
|
+
----
|
|
307
|
+
class MemberOnly < Lutaml::Model::Serializable
|
|
308
|
+
attribute :items, Concept, collection: true
|
|
309
|
+
|
|
310
|
+
rdf do
|
|
311
|
+
namespace SkosNamespace
|
|
312
|
+
members :items
|
|
313
|
+
end
|
|
314
|
+
end
|
|
315
|
+
----
|
|
316
|
+
|
|
317
|
+
=== Namespace Merging
|
|
318
|
+
|
|
319
|
+
When a container and its members declare different namespaces, all namespaces
|
|
320
|
+
are merged in the output. Prefix declarations in Turtle and `@context` entries
|
|
321
|
+
in JSON-LD include the union of all namespaces.
|
|
322
|
+
|
|
323
|
+
== Architecture
|
|
324
|
+
|
|
325
|
+
The unified RDF infrastructure consists of:
|
|
326
|
+
|
|
327
|
+
* `Lutaml::Rdf::Mapping` — Unified mapping base class with `namespace`,
|
|
328
|
+
`subject`, `type`, `predicate`, and `members` DSL methods
|
|
329
|
+
* `Lutaml::Rdf::MappingRule` — Value object for predicate-to-attribute mappings
|
|
330
|
+
* `Lutaml::Rdf::MemberRule` — Value object for `members` declarations
|
|
331
|
+
* `Lutaml::Rdf::Transform` — Base transform class with shared logic for subject
|
|
332
|
+
URI resolution, type resolution, and language extraction
|
|
333
|
+
* `Lutaml::Turtle::Transform` — Inherits from `Rdf::Transform`, produces Turtle
|
|
334
|
+
* `Lutaml::JsonLd::Transform` — Inherits from `Rdf::Transform`, produces JSON-LD
|
|
335
|
+
|
|
336
|
+
Both format-specific transforms detect `Rdf::Mapping` instances and dispatch to
|
|
337
|
+
unified serialization logic.
|
|
338
|
+
|
|
339
|
+
== Error Handling
|
|
340
|
+
|
|
341
|
+
* `Lutaml::Turtle::MissingSubjectError` — raised when a model with predicates
|
|
342
|
+
has no `subject` block and no `members` declaration
|
|
343
|
+
* `ArgumentError` — raised when a predicate's `namespace` is not a
|
|
344
|
+
`Rdf::Namespace` subclass
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Turtle Serialization
|
|
3
|
+
nav_order: 16
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
= Turtle Serialization
|
|
7
|
+
|
|
8
|
+
Lutaml::Model supports serialization to and from W3C RDF Turtle format using
|
|
9
|
+
SKOS, Dublin Core Terms, and other W3C vocabularies.
|
|
10
|
+
|
|
11
|
+
== Setup
|
|
12
|
+
|
|
13
|
+
Add the `rdf-turtle` gem to your Gemfile:
|
|
14
|
+
|
|
15
|
+
[source,ruby]
|
|
16
|
+
----
|
|
17
|
+
gem "rdf-turtle", "~> 3.3"
|
|
18
|
+
----
|
|
19
|
+
|
|
20
|
+
Turtle is a **non-key-value** format adapter with its own mapping DSL based on
|
|
21
|
+
RDF triples (subject–predicate–object). It is registered as a first-class
|
|
22
|
+
format in the `FormatRegistry` with dedicated `Mapping`, `Transform`, and
|
|
23
|
+
`Adapter` classes.
|
|
24
|
+
|
|
25
|
+
== Mapping DSL
|
|
26
|
+
|
|
27
|
+
Use the `turtle do` block in your model to define Turtle mappings:
|
|
28
|
+
|
|
29
|
+
[source,ruby]
|
|
30
|
+
----
|
|
31
|
+
class Concept < Lutaml::Model::Serializable
|
|
32
|
+
attribute :name, :string
|
|
33
|
+
attribute :description, :string
|
|
34
|
+
attribute :code, :string
|
|
35
|
+
|
|
36
|
+
turtle do
|
|
37
|
+
namespace Lutaml::Rdf::Namespaces::SkosNamespace,
|
|
38
|
+
Lutaml::Rdf::Namespaces::DctermsNamespace
|
|
39
|
+
|
|
40
|
+
subject { |m| "http://example.org/concept/#{m.code}" }
|
|
41
|
+
type "skos:Concept"
|
|
42
|
+
|
|
43
|
+
predicate :prefLabel,
|
|
44
|
+
namespace: Lutaml::Rdf::Namespaces::SkosNamespace,
|
|
45
|
+
to: :name,
|
|
46
|
+
lang_tagged: true
|
|
47
|
+
|
|
48
|
+
predicate :definition,
|
|
49
|
+
namespace: Lutaml::Rdf::Namespaces::SkosNamespace,
|
|
50
|
+
to: :description
|
|
51
|
+
|
|
52
|
+
predicate :notation,
|
|
53
|
+
namespace: Lutaml::Rdf::Namespaces::SkosNamespace,
|
|
54
|
+
to: :code
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
----
|
|
58
|
+
|
|
59
|
+
=== namespace
|
|
60
|
+
|
|
61
|
+
Declares the `@prefix` lines in the output. Accepts one or more
|
|
62
|
+
`Lutaml::Rdf::Namespace` subclass references. Internally builds a
|
|
63
|
+
`NamespaceSet` for O(1) prefix lookup and IRI resolution.
|
|
64
|
+
|
|
65
|
+
=== subject
|
|
66
|
+
|
|
67
|
+
Required for top-level models with predicates and no `members`. A block that
|
|
68
|
+
generates the subject URI from the model instance. Raises
|
|
69
|
+
`Lutaml::Turtle::MissingSubjectError` if not defined when needed.
|
|
70
|
+
|
|
71
|
+
Member models (serialized via a container's `members` declaration) can omit the
|
|
72
|
+
`subject` block — blank nodes are used automatically.
|
|
73
|
+
|
|
74
|
+
=== type
|
|
75
|
+
|
|
76
|
+
Sets the RDF type (`rdf:type`) triple. Compact IRIs (e.g., `"skos:Concept"`)
|
|
77
|
+
are resolved to full URIs via the declared namespaces. Full URIs (e.g.,
|
|
78
|
+
`"http://example.org/MyType"`) are used as-is.
|
|
79
|
+
|
|
80
|
+
=== predicate
|
|
81
|
+
|
|
82
|
+
Each `predicate` creates a `Lutaml::Rdf::MappingRule` value object:
|
|
83
|
+
|
|
84
|
+
* `name` — the local name in the namespace (e.g., `:prefLabel`)
|
|
85
|
+
* `namespace:` — the `Lutaml::Rdf::Namespace` subclass (required, validated)
|
|
86
|
+
* `to:` — the model attribute to read/write (required)
|
|
87
|
+
* `lang_tagged:` (default: `false`) — if true, appends `@lang` suffix from the
|
|
88
|
+
value's `language_code` or `language` method
|
|
89
|
+
|
|
90
|
+
Validation errors:
|
|
91
|
+
|
|
92
|
+
* `namespace` must be a `Rdf::Namespace` subclass — raises `ArgumentError`
|
|
93
|
+
* `to:` is required — raises `ArgumentError`
|
|
94
|
+
|
|
95
|
+
== Serialization
|
|
96
|
+
|
|
97
|
+
[source,ruby]
|
|
98
|
+
----
|
|
99
|
+
concept = Concept.new(name: "test", description: "A description", code: "2119")
|
|
100
|
+
turtle = concept.to_turtle
|
|
101
|
+
----
|
|
102
|
+
|
|
103
|
+
Produces:
|
|
104
|
+
|
|
105
|
+
[source,turtle]
|
|
106
|
+
----
|
|
107
|
+
@prefix skos: <http://www.w3.org/2004/02/skos/core#> .
|
|
108
|
+
|
|
109
|
+
<http://example.org/concept/2119> a skos:Concept;
|
|
110
|
+
skos:definition "A description";
|
|
111
|
+
skos:notation "2119";
|
|
112
|
+
skos:prefLabel "test" .
|
|
113
|
+
----
|
|
114
|
+
|
|
115
|
+
The output uses the `RDF::Turtle::Writer` from the `rdf-turtle` gem, which
|
|
116
|
+
automatically compacts URIs using declared prefixes and uses native Turtle
|
|
117
|
+
syntax for typed literals.
|
|
118
|
+
|
|
119
|
+
=== Typed values
|
|
120
|
+
|
|
121
|
+
Integer and boolean attributes are serialized using native Turtle literal
|
|
122
|
+
syntax:
|
|
123
|
+
|
|
124
|
+
* Integers → `42` (not `"42"^^xsd:integer`)
|
|
125
|
+
* Booleans → `true` / `false` (not `"true"^^xsd:boolean`)
|
|
126
|
+
|
|
127
|
+
=== Collection attributes
|
|
128
|
+
|
|
129
|
+
When an attribute is defined with `collection: true`, each value produces a
|
|
130
|
+
separate triple with the same predicate. The writer may use comma-separated
|
|
131
|
+
object syntax:
|
|
132
|
+
|
|
133
|
+
[source,turtle]
|
|
134
|
+
----
|
|
135
|
+
<http://example.org/1> skos:prefLabel "en", "fr" .
|
|
136
|
+
----
|
|
137
|
+
|
|
138
|
+
== Special Characters
|
|
139
|
+
|
|
140
|
+
String values containing quotes, newlines, tabs, or backslashes are
|
|
141
|
+
automatically escaped. The `RDF::Turtle::Writer` uses triple-quoted strings
|
|
142
|
+
(`"""..."""`) for multi-line literals.
|
|
143
|
+
|
|
144
|
+
== Nil Values
|
|
145
|
+
|
|
146
|
+
Predicates for attributes with `nil` values are omitted from the output. If
|
|
147
|
+
all predicates produce no data, the result is an empty string.
|
|
148
|
+
|
|
149
|
+
== Deserialization
|
|
150
|
+
|
|
151
|
+
[source,ruby]
|
|
152
|
+
----
|
|
153
|
+
concept = Concept.from_turtle(turtle_string)
|
|
154
|
+
puts concept.code # => "2119"
|
|
155
|
+
puts concept.description # => "A description"
|
|
156
|
+
----
|
|
157
|
+
|
|
158
|
+
The `from_turtle` method:
|
|
159
|
+
|
|
160
|
+
. Parses the Turtle string into an `RDF::Graph` via `RDF::Turtle::Reader`
|
|
161
|
+
. Finds subjects by `rdf:type` matching the declared type
|
|
162
|
+
. Maps predicates back to model attributes using the declared predicate rules
|
|
163
|
+
. Converts RDF typed literals back to Ruby types (integers, booleans, etc.)
|
|
164
|
+
|
|
165
|
+
Language-tagged literals are extracted without the language tag.
|
|
166
|
+
|
|
167
|
+
== Round-trip
|
|
168
|
+
|
|
169
|
+
Model data round-trips through Turtle serialization:
|
|
170
|
+
|
|
171
|
+
[source,ruby]
|
|
172
|
+
----
|
|
173
|
+
restored = Concept.from_turtle(concept.to_turtle)
|
|
174
|
+
restored.code == concept.code # => true
|
|
175
|
+
restored.description == concept.description # => true
|
|
176
|
+
----
|
|
177
|
+
|
|
178
|
+
== Error Handling
|
|
179
|
+
|
|
180
|
+
* `Lutaml::Turtle::MissingSubjectError` — raised when serializing a model
|
|
181
|
+
without a `subject` block defined
|
|
182
|
+
* `RDF::ReaderError` — raised by the RDF parser for malformed Turtle input
|
|
183
|
+
* Both are wrapped in `Lutaml::Model::InvalidFormatError` by the format
|
|
184
|
+
pipeline
|
|
185
|
+
|
|
186
|
+
== Architecture
|
|
187
|
+
|
|
188
|
+
The Turtle format is composed of:
|
|
189
|
+
|
|
190
|
+
* `Lutaml::Turtle::Adapter` — parses Turtle strings to `RDF::Graph` and
|
|
191
|
+
serializes graphs back to Turtle
|
|
192
|
+
* `Lutaml::Turtle::Mapping` — empty subclass of `Rdf::Mapping`; inherits
|
|
193
|
+
`namespace`, `subject`, `type`, `predicate`, `members` DSL methods
|
|
194
|
+
* `Lutaml::Turtle::Transform` — inherits from `Rdf::Transform`; bidirectional
|
|
195
|
+
transform between model instances and RDF graphs via `model_to_data` /
|
|
196
|
+
`data_to_model`
|
|
197
|
+
|
|
198
|
+
== Unified `rdf` DSL
|
|
199
|
+
|
|
200
|
+
If you need both JSON-LD and Turtle output from the same model, use the
|
|
201
|
+
unified `rdf` DSL instead of separate `jsonld` and `turtle` blocks:
|
|
202
|
+
|
|
203
|
+
[source,ruby]
|
|
204
|
+
----
|
|
205
|
+
class Concept < Lutaml::Model::Serializable
|
|
206
|
+
attribute :name, :string
|
|
207
|
+
attribute :code, :string
|
|
208
|
+
|
|
209
|
+
rdf do
|
|
210
|
+
namespace Lutaml::Rdf::Namespaces::SkosNamespace
|
|
211
|
+
subject { |m| "http://example.org/#{m.code}" }
|
|
212
|
+
type "skos:Concept"
|
|
213
|
+
predicate :prefLabel, namespace: SkosNamespace, to: :name
|
|
214
|
+
predicate :notation, namespace: SkosNamespace, to: :code
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
----
|
|
218
|
+
|
|
219
|
+
The `rdf` block also supports `members` for graph-level serialization, where a
|
|
220
|
+
container model emits all member resources as separate subjects in the same
|
|
221
|
+
Turtle document.
|
|
222
|
+
|
|
223
|
+
See link:../_guides/rdf-serialization.adoc[Unified RDF Serialization] for the
|
|
224
|
+
complete guide.
|
|
@@ -25,6 +25,8 @@ LutaML, out of the box, supports the following serialization formats:
|
|
|
25
25
|
* YAML (https://yaml.org/[YAML version 1.2])
|
|
26
26
|
* 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])
|
|
27
27
|
* TOML (https://toml.io/en[TOML version 1.0])
|
|
28
|
+
* JSON-LD (https://www.w3.org/TR/json-ld11/[W3C JSON-LD 1.1])
|
|
29
|
+
* Turtle (https://www.w3.org/TR/turtle/[W3C RDF 1.1 Turtle])
|
|
28
30
|
|
|
29
31
|
The adapter interface is also used to support certain transformation of models
|
|
30
32
|
into an "end format", which is not a serialization format. For example, the
|
|
@@ -309,6 +311,35 @@ end
|
|
|
309
311
|
----
|
|
310
312
|
|
|
311
313
|
|
|
314
|
+
=== JSON-LD
|
|
315
|
+
|
|
316
|
+
Lutaml::Model supports serialization to and from JSON-LD (W3C JSON-LD 1.1).
|
|
317
|
+
JSON-LD is a key-value format that extends the built-in JSON serialization with
|
|
318
|
+
JSON-LD-specific constructs: `@context`, `@type`, and `@id`.
|
|
319
|
+
|
|
320
|
+
The adapter uses the standard Ruby `JSON` library internally — no additional gem
|
|
321
|
+
is required beyond `lutaml-model`.
|
|
322
|
+
|
|
323
|
+
See the link:../guides/jsonld-serialization[JSON-LD Serialization guide] for
|
|
324
|
+
mapping DSL details and usage examples.
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
=== Turtle
|
|
328
|
+
|
|
329
|
+
Lutaml::Model supports serialization to and from W3C RDF Turtle format.
|
|
330
|
+
Turtle is a non-key-value format based on RDF triples with its own mapping DSL.
|
|
331
|
+
|
|
332
|
+
Requires the `rdf-turtle` gem:
|
|
333
|
+
|
|
334
|
+
[source,ruby]
|
|
335
|
+
----
|
|
336
|
+
gem "rdf-turtle", "~> 3.3"
|
|
337
|
+
----
|
|
338
|
+
|
|
339
|
+
See the link:../guides/turtle-serialization[Turtle Serialization guide] for
|
|
340
|
+
mapping DSL details and usage examples.
|
|
341
|
+
|
|
342
|
+
|
|
312
343
|
=== Error handling
|
|
313
344
|
|
|
314
345
|
When parsing invalid serialization format data, an `InvalidFormatError` is
|
data/docs/_references/index.adoc
CHANGED
|
@@ -39,6 +39,7 @@ Deep dives into specific features:
|
|
|
39
39
|
|
|
40
40
|
* link:../format-independent-features[Format-Independent Features] - Cross-format mechanisms
|
|
41
41
|
* link:../parent-root-context[Parent and Root Context] - Model hierarchy navigation
|
|
42
|
+
* link:../rdf-namespaces[RDF Namespaces] - Namespace, NamespaceSet, Iri, Literal
|
|
42
43
|
|
|
43
44
|
== See also
|
|
44
45
|
|