lutaml-model 0.8.3 → 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 +16 -22
- data/Gemfile +2 -0
- data/README.adoc +327 -3
- data/docs/_guides/document-validation.adoc +303 -0
- data/docs/_guides/index.adoc +19 -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/_guides/xml-mapping.adoc +9 -1
- data/docs/_guides/xml_mappings/07_best_practices.adoc +36 -0
- data/docs/_guides/xml_mappings/08_troubleshooting.adoc +89 -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/_tutorials/lutaml-xml-architecture.adoc +6 -1
- 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/attribute.rb +19 -1
- data/lib/lutaml/model/error/liquid_drop_already_registered_error.rb +11 -0
- data/lib/lutaml/model/error/ordered_content_mapping_error.rb +17 -0
- data/lib/lutaml/model/format_registry.rb +10 -1
- data/lib/lutaml/model/global_context.rb +1 -0
- data/lib/lutaml/model/liquefiable.rb +12 -15
- data/lib/lutaml/model/mapping/mapping_rule.rb +10 -2
- data/lib/lutaml/model/mapping_hash.rb +1 -1
- data/lib/lutaml/model/serialize/format_conversion.rb +17 -1
- data/lib/lutaml/model/services/transformer.rb +67 -32
- data/lib/lutaml/model/transform.rb +41 -4
- data/lib/lutaml/model/uninitialized_class.rb +11 -5
- data/lib/lutaml/model/validation/concerns/has_issues.rb +27 -0
- data/lib/lutaml/model/validation/context.rb +36 -0
- data/lib/lutaml/model/validation/issue.rb +62 -0
- data/lib/lutaml/model/validation/layer_result.rb +34 -0
- data/lib/lutaml/model/validation/profile.rb +66 -0
- data/lib/lutaml/model/validation/registry.rb +60 -0
- data/lib/lutaml/model/validation/remediation.rb +33 -0
- data/lib/lutaml/model/validation/remediation_result.rb +20 -0
- data/lib/lutaml/model/validation/report.rb +39 -0
- data/lib/lutaml/model/validation/rule.rb +59 -0
- data/lib/lutaml/model/validation.rb +2 -1
- data/lib/lutaml/model/validation_framework.rb +77 -0
- data/lib/lutaml/model/version.rb +1 -1
- data/lib/lutaml/model.rb +10 -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/lib/lutaml/xml/adapter/nokogiri_adapter.rb +9 -2
- data/lib/lutaml/xml/adapter/oga_adapter.rb +11 -3
- data/lib/lutaml/xml/adapter/ox_adapter.rb +5 -2
- data/lib/lutaml/xml/adapter/rexml_adapter.rb +10 -3
- data/lib/lutaml/xml/adapter_element.rb +26 -2
- data/lib/lutaml/xml/data_model.rb +14 -0
- data/lib/lutaml/xml/document.rb +3 -0
- data/lib/lutaml/xml/element.rb +8 -2
- data/lib/lutaml/xml/mapping.rb +9 -0
- data/lib/lutaml/xml/model_transform.rb +42 -0
- data/lib/lutaml/xml/schema/xsd/base.rb +4 -1
- data/lib/lutaml/xml/serialization/instance_methods.rb +3 -1
- data/lib/lutaml/xml/transformation/ordered_applier.rb +46 -2
- data/lib/lutaml/xml/transformation.rb +40 -1
- data/lib/lutaml/xml/xml_element.rb +8 -7
- data/lutaml-model.gemspec +1 -1
- 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/model/attribute_default_cache_spec.rb +58 -0
- data/spec/lutaml/model/liquefiable_spec.rb +22 -6
- data/spec/lutaml/model/liquid_compatibility_spec.rb +442 -0
- data/spec/lutaml/model/ordered_content_spec.rb +5 -5
- data/spec/lutaml/model/services/transformer_spec.rb +43 -0
- data/spec/lutaml/model/transform_cache_spec.rb +62 -0
- data/spec/lutaml/model/transform_dynamic_attributes_spec.rb +41 -0
- data/spec/lutaml/model/uninitialized_class_deep_dup_spec.rb +39 -0
- data/spec/lutaml/model/uninitialized_class_spec.rb +14 -2
- data/spec/lutaml/model/validation/concerns/has_issues_spec.rb +76 -0
- data/spec/lutaml/model/validation/context_spec.rb +60 -0
- data/spec/lutaml/model/validation/issue_spec.rb +77 -0
- data/spec/lutaml/model/validation/layer_result_spec.rb +66 -0
- data/spec/lutaml/model/validation/profile_spec.rb +134 -0
- data/spec/lutaml/model/validation/registry_spec.rb +94 -0
- data/spec/lutaml/model/validation/remediation_result_spec.rb +23 -0
- data/spec/lutaml/model/validation/remediation_spec.rb +72 -0
- data/spec/lutaml/model/validation/report_spec.rb +58 -0
- data/spec/lutaml/model/validation/rule_spec.rb +134 -0
- data/spec/lutaml/model/validation/uninitialized_class_validate_spec.rb +29 -0
- data/spec/lutaml/model/validation/validation_error_spec.rb +29 -0
- data/spec/lutaml/model/validation/validation_framework_spec.rb +110 -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
- data/spec/lutaml/xml/content_model_validation_spec.rb +157 -0
- data/spec/lutaml/xml/mapping_spec.rb +12 -7
- metadata +95 -7
- data/spec/fixtures/liquid_templates/_ceramics.liquid +0 -3
|
@@ -8,6 +8,95 @@ parent: XML Mappings Guide
|
|
|
8
8
|
|
|
9
9
|
== Common errors
|
|
10
10
|
|
|
11
|
+
=== OrderedContentMappingError
|
|
12
|
+
|
|
13
|
+
**Error message:**
|
|
14
|
+
|
|
15
|
+
----
|
|
16
|
+
Lutaml::Model::OrderedContentMappingError: Element-only content model
|
|
17
|
+
(`ordered`) does not support `map_content` in ModelName. Use `mixed_content`
|
|
18
|
+
instead of `ordered` when you need to capture text content between elements.
|
|
19
|
+
----
|
|
20
|
+
|
|
21
|
+
**Cause:**
|
|
22
|
+
|
|
23
|
+
Using `map_content` (text content mapping) together with `ordered`
|
|
24
|
+
(element-only content model). Element-only models only contain child elements,
|
|
25
|
+
not text nodes.
|
|
26
|
+
|
|
27
|
+
**Solution:**
|
|
28
|
+
|
|
29
|
+
If you need text content between elements, use `mixed_content` instead of
|
|
30
|
+
`ordered`:
|
|
31
|
+
|
|
32
|
+
[source,ruby]
|
|
33
|
+
----
|
|
34
|
+
# WRONG -- ordered + map_content is invalid
|
|
35
|
+
class Paragraph < Lutaml::Model::Serializable
|
|
36
|
+
xml do
|
|
37
|
+
element 'p'
|
|
38
|
+
ordered # Element-only, no text allowed
|
|
39
|
+
map_content to: :text # ERROR!
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# CORRECT -- use mixed_content for text + elements
|
|
44
|
+
class Paragraph < Lutaml::Model::Serializable
|
|
45
|
+
attribute :text, :string, collection: true
|
|
46
|
+
|
|
47
|
+
xml do
|
|
48
|
+
element 'p'
|
|
49
|
+
mixed_content # Allows text + elements
|
|
50
|
+
map_content to: :text # OK!
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
----
|
|
54
|
+
|
|
55
|
+
=== MixedContentCollectionError
|
|
56
|
+
|
|
57
|
+
**Error message:**
|
|
58
|
+
|
|
59
|
+
----
|
|
60
|
+
Lutaml::Model::MixedContentCollectionError: Mixed content requires `text`
|
|
61
|
+
to be a string collection in ModelName. Use `attribute :text, :string,
|
|
62
|
+
collection: true` when `mixed_content` is enabled.
|
|
63
|
+
----
|
|
64
|
+
|
|
65
|
+
**Cause:**
|
|
66
|
+
|
|
67
|
+
Using `mixed_content` with `map_content` mapped to a non-collection attribute.
|
|
68
|
+
Mixed content produces multiple text segments (one between each pair of child
|
|
69
|
+
elements), so the target attribute must be a collection.
|
|
70
|
+
|
|
71
|
+
**Solution:**
|
|
72
|
+
|
|
73
|
+
Declare the attribute with `collection: true`:
|
|
74
|
+
|
|
75
|
+
[source,ruby]
|
|
76
|
+
----
|
|
77
|
+
# WRONG -- non-collection attribute with mixed content
|
|
78
|
+
class Paragraph < Lutaml::Model::Serializable
|
|
79
|
+
attribute :text, :string # Missing collection: true
|
|
80
|
+
|
|
81
|
+
xml do
|
|
82
|
+
element 'p'
|
|
83
|
+
mixed_content
|
|
84
|
+
map_content to: :text # ERROR!
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# CORRECT -- collection attribute for mixed content
|
|
89
|
+
class Paragraph < Lutaml::Model::Serializable
|
|
90
|
+
attribute :text, :string, collection: true
|
|
91
|
+
|
|
92
|
+
xml do
|
|
93
|
+
element 'p'
|
|
94
|
+
mixed_content
|
|
95
|
+
map_content to: :text # OK!
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
----
|
|
99
|
+
|
|
11
100
|
=== NoRootMappingError
|
|
12
101
|
|
|
13
102
|
**Error message:**
|
|
@@ -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
|
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: RDF Namespaces
|
|
3
|
+
nav_order: 12
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
= RDF Namespaces
|
|
7
|
+
|
|
8
|
+
Lutaml::Model provides a comprehensive RDF infrastructure with `Namespace`,
|
|
9
|
+
`NamespaceSet`, `Iri`, and `Literal` classes. These are shared by the JSON-LD
|
|
10
|
+
and Turtle format adapters.
|
|
11
|
+
|
|
12
|
+
== Namespace
|
|
13
|
+
|
|
14
|
+
`Lutaml::Rdf::Namespace` is the abstract base class for RDF namespace
|
|
15
|
+
vocabularies. Subclasses define `uri` and `prefix` at the class level:
|
|
16
|
+
|
|
17
|
+
[source,ruby]
|
|
18
|
+
----
|
|
19
|
+
ns = Class.new(Lutaml::Rdf::Namespace)
|
|
20
|
+
ns.uri "http://example.org/ns/"
|
|
21
|
+
ns.prefix "ex"
|
|
22
|
+
|
|
23
|
+
ns["someName"] # => "http://example.org/ns/someName"
|
|
24
|
+
ns.prefixed("Name") # => "ex:Name"
|
|
25
|
+
----
|
|
26
|
+
|
|
27
|
+
=== Immutability
|
|
28
|
+
|
|
29
|
+
Once set, `uri` and `prefix` are frozen and cannot be changed:
|
|
30
|
+
|
|
31
|
+
[source,ruby]
|
|
32
|
+
----
|
|
33
|
+
ns = Class.new(Lutaml::Rdf::Namespace)
|
|
34
|
+
ns.uri "http://example.org/ns/"
|
|
35
|
+
ns.uri "http://other.org/" # => raises FrozenError
|
|
36
|
+
----
|
|
37
|
+
|
|
38
|
+
Each subclass has independent state — setting `uri` on one does not affect
|
|
39
|
+
another.
|
|
40
|
+
|
|
41
|
+
=== Equality
|
|
42
|
+
|
|
43
|
+
Namespace classes implement equality based on `uri` and `prefix`:
|
|
44
|
+
|
|
45
|
+
[source,ruby]
|
|
46
|
+
----
|
|
47
|
+
ns1 = Class.new(Lutaml::Rdf::Namespace)
|
|
48
|
+
ns1.uri "http://example.org/"
|
|
49
|
+
ns1.prefix "ex"
|
|
50
|
+
|
|
51
|
+
ns2 = Class.new(Lutaml::Rdf::Namespace)
|
|
52
|
+
ns2.uri "http://example.org/"
|
|
53
|
+
ns2.prefix "ex"
|
|
54
|
+
|
|
55
|
+
ns1 == ns2 # => true
|
|
56
|
+
----
|
|
57
|
+
|
|
58
|
+
=== Compact IRI resolution
|
|
59
|
+
|
|
60
|
+
[source,ruby]
|
|
61
|
+
----
|
|
62
|
+
Lutaml::Rdf::Namespace.resolve_compact_iri("skos:Concept", [SkosNamespace])
|
|
63
|
+
# => "http://www.w3.org/2004/02/skos/core#Concept"
|
|
64
|
+
|
|
65
|
+
Lutaml::Rdf::Namespace.resolve_compact_iri("plain_name", [SkosNamespace])
|
|
66
|
+
# => "plain_name" (returned as-is when no colon present)
|
|
67
|
+
----
|
|
68
|
+
|
|
69
|
+
== NamespaceSet
|
|
70
|
+
|
|
71
|
+
`Lutaml::Rdf::NamespaceSet` is an ordered, enumerable collection of namespace
|
|
72
|
+
classes with O(1) prefix lookup:
|
|
73
|
+
|
|
74
|
+
[source,ruby]
|
|
75
|
+
----
|
|
76
|
+
set = Lutaml::Rdf::NamespaceSet.new(
|
|
77
|
+
Lutaml::Rdf::Namespaces::SkosNamespace,
|
|
78
|
+
Lutaml::Rdf::Namespaces::DctermsNamespace
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
set["skos"] # => SkosNamespace (O(1) lookup)
|
|
82
|
+
set.size # => 2
|
|
83
|
+
set.resolve_compact_iri("skos:Concept")
|
|
84
|
+
# => "http://www.w3.org/2004/02/skos/core#Concept"
|
|
85
|
+
set.compact("http://www.w3.org/2004/02/skos/core#Concept")
|
|
86
|
+
# => "skos:Concept"
|
|
87
|
+
set.to_hash
|
|
88
|
+
# => { "skos" => "http://www.w3.org/2004/02/skos/core#",
|
|
89
|
+
# "dcterms" => "http://purl.org/dc/terms/" }
|
|
90
|
+
----
|
|
91
|
+
|
|
92
|
+
=== Collision detection
|
|
93
|
+
|
|
94
|
+
Adding two different namespace classes with the same prefix raises an error:
|
|
95
|
+
|
|
96
|
+
[source,ruby]
|
|
97
|
+
----
|
|
98
|
+
set = Lutaml::Rdf::NamespaceSet.new(SkosNamespace)
|
|
99
|
+
ns = Class.new(Lutaml::Rdf::Namespace)
|
|
100
|
+
ns.uri "http://other.org/"
|
|
101
|
+
ns.prefix "skos"
|
|
102
|
+
set.add(ns) # => raises ArgumentError: Prefix 'skos' conflicts
|
|
103
|
+
----
|
|
104
|
+
|
|
105
|
+
Adding the same class twice is allowed (idempotent).
|
|
106
|
+
|
|
107
|
+
== Iri
|
|
108
|
+
|
|
109
|
+
`Lutaml::Rdf::Iri` is an immutable value object for IRIs:
|
|
110
|
+
|
|
111
|
+
[source,ruby]
|
|
112
|
+
----
|
|
113
|
+
iri = Lutaml::Rdf::Iri.new("http://example.org/concept/1")
|
|
114
|
+
iri.to_s # => "http://example.org/concept/1"
|
|
115
|
+
iri.value # => "http://example.org/concept/1" (frozen)
|
|
116
|
+
|
|
117
|
+
# Expand compact → full
|
|
118
|
+
set = Lutaml::Rdf::NamespaceSet.new(SkosNamespace)
|
|
119
|
+
compact_iri = Lutaml::Rdf::Iri.new("skos:Concept")
|
|
120
|
+
compact_iri.expand(set) # => Iri("http://www.w3.org/2004/02/skos/core#Concept")
|
|
121
|
+
|
|
122
|
+
# Compact full → compact
|
|
123
|
+
full_iri = Lutaml::Rdf::Iri.new("http://www.w3.org/2004/02/skos/core#Concept")
|
|
124
|
+
full_iri.compact(set) # => "skos:Concept"
|
|
125
|
+
----
|
|
126
|
+
|
|
127
|
+
Implements `Comparable` for sorting.
|
|
128
|
+
|
|
129
|
+
== Literal
|
|
130
|
+
|
|
131
|
+
`Lutaml::Rdf::Literal` is a value object for RDF literals with optional
|
|
132
|
+
datatype and language tag:
|
|
133
|
+
|
|
134
|
+
[source,ruby]
|
|
135
|
+
----
|
|
136
|
+
# Plain literal
|
|
137
|
+
lit = Lutaml::Rdf::Literal.new("hello")
|
|
138
|
+
lit.to_turtle # => '"hello"'
|
|
139
|
+
lit.to_jsonld_term # => "hello"
|
|
140
|
+
|
|
141
|
+
# Language-tagged literal
|
|
142
|
+
lit = Lutaml::Rdf::Literal.new("hello", language: "en")
|
|
143
|
+
lit.to_turtle # => '"hello"@en'
|
|
144
|
+
lit.to_jsonld_term # => { "@value" => "hello", "@language" => "en" }
|
|
145
|
+
|
|
146
|
+
# Typed literal
|
|
147
|
+
lit = Lutaml::Rdf::Literal.new("42", datatype: "http://www.w3.org/2001/XMLSchema#integer")
|
|
148
|
+
lit.to_turtle # => '"42"^^<http://www.w3.org/2001/XMLSchema#integer>'
|
|
149
|
+
lit.to_jsonld_term # => { "@value" => "42", "@type" => "xsd:integer" }
|
|
150
|
+
----
|
|
151
|
+
|
|
152
|
+
=== Special character escaping
|
|
153
|
+
|
|
154
|
+
Quotes, newlines, tabs, and backslashes are automatically escaped in Turtle
|
|
155
|
+
and JSON-LD output.
|
|
156
|
+
|
|
157
|
+
== Standard W3C Namespaces
|
|
158
|
+
|
|
159
|
+
Pre-defined namespace classes are in `Lutaml::Rdf::Namespaces`:
|
|
160
|
+
|
|
161
|
+
[cols="1,1,1"]
|
|
162
|
+
|===
|
|
163
|
+
| Class | Prefix | URI
|
|
164
|
+
|
|
165
|
+
| `SkosNamespace` | `skos` | `http://www.w3.org/2004/02/skos/core#`
|
|
166
|
+
| `DctermsNamespace` | `dcterms` | `http://purl.org/dc/terms/`
|
|
167
|
+
| `RdfSyntaxNamespace` | `rdf` | `http://www.w3.org/1999/02/22-rdf-syntax-ns#`
|
|
168
|
+
| `RdfsNamespace` | `rdfs` | `http://www.w3.org/2000/01/rdf-schema#`
|
|
169
|
+
| `OwlNamespace` | `owl` | `http://www.w3.org/2002/07/owl#`
|
|
170
|
+
| `XsdNamespace` | `xsd` | `http://www.w3.org/2001/XMLSchema#`
|
|
171
|
+
|===
|
|
172
|
+
|
|
173
|
+
NOTE: `RdfNamespace` is a backward-compatible alias for `RdfSyntaxNamespace`.
|
|
174
|
+
|
|
175
|
+
== Custom Namespaces
|
|
176
|
+
|
|
177
|
+
Define your own by subclassing:
|
|
178
|
+
|
|
179
|
+
[source,ruby]
|
|
180
|
+
----
|
|
181
|
+
class MyNamespace < Lutaml::Rdf::Namespace
|
|
182
|
+
uri "http://example.org/vocab/"
|
|
183
|
+
prefix "my"
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
MyNamespace["term"] # => "http://example.org/vocab/term"
|
|
187
|
+
----
|
|
188
|
+
|
|
189
|
+
== Error hierarchy
|
|
190
|
+
|
|
191
|
+
[cols="1,1"]
|
|
192
|
+
|===
|
|
193
|
+
| Class | Description
|
|
194
|
+
|
|
195
|
+
| `Lutaml::Rdf::Error` | Base error for all RDF errors
|
|
196
|
+
| `Lutaml::Turtle::MissingSubjectError` | Raised when Turtle mapping has no `subject` block
|
|
197
|
+
|===
|
|
198
|
+
|
|
199
|
+
== Unified RDF Mapping
|
|
200
|
+
|
|
201
|
+
`Lutaml::Rdf::Mapping` is the unified mapping base class shared by both Turtle
|
|
202
|
+
and JSON-LD format adapters. It provides the `rdf` DSL for defining
|
|
203
|
+
predicate-based mappings once and using them for both output formats.
|
|
204
|
+
|
|
205
|
+
See link:../_guides/rdf-serialization.adoc[Unified RDF Serialization] for the
|
|
206
|
+
complete guide.
|
|
207
|
+
|
|
208
|
+
=== MappingRule
|
|
209
|
+
|
|
210
|
+
`Lutaml::Rdf::MappingRule` is a value object for predicate-to-attribute
|
|
211
|
+
mappings:
|
|
212
|
+
|
|
213
|
+
[source,ruby]
|
|
214
|
+
----
|
|
215
|
+
# Created by the `predicate` DSL method
|
|
216
|
+
predicate :prefLabel, namespace: SkosNamespace, to: :name, lang_tagged: true
|
|
217
|
+
----
|
|
218
|
+
|
|
219
|
+
Each rule stores: `predicate_name`, `namespace`, `to` (attribute symbol), and
|
|
220
|
+
`lang_tagged` flag.
|
|
221
|
+
|
|
222
|
+
=== MemberRule
|
|
223
|
+
|
|
224
|
+
`Lutaml::Rdf::MemberRule` is a value object for the `members` declaration in
|
|
225
|
+
graph-level RDF mapping:
|
|
226
|
+
|
|
227
|
+
[source,ruby]
|
|
228
|
+
----
|
|
229
|
+
# In a container model's rdf block
|
|
230
|
+
members :concepts
|
|
231
|
+
----
|
|
232
|
+
|
|
233
|
+
Stores the attribute name referencing the collection of member models.
|
|
234
|
+
|
|
235
|
+
=== Transform
|
|
236
|
+
|
|
237
|
+
`Lutaml::Rdf::Transform` is the base transform class for RDF serialization,
|
|
238
|
+
shared by both `Turtle::Transform` and `JsonLd::Transform`. It provides:
|
|
239
|
+
|
|
240
|
+
* `resolve_subject_uri(mapping, instance)` — evaluates the subject block
|
|
241
|
+
* `resolve_type_uri(mapping)` — resolves compact IRI to full URI
|
|
242
|
+
* `resolve_type_compact(mapping)` — returns the compact IRI form
|
|
243
|
+
* `extract_language(value)` — extracts language code from `LanguageTagged` values
|
|
@@ -690,7 +690,8 @@ XmlElement = {
|
|
|
690
690
|
attributes: { orderDate: "1999-10-20" },
|
|
691
691
|
elements: [
|
|
692
692
|
XmlElement(name: "shipTo", ...),
|
|
693
|
-
XmlText("Text content")
|
|
693
|
+
XmlText("Text content"),
|
|
694
|
+
XmlComment(" comment text ")
|
|
694
695
|
],
|
|
695
696
|
content: "Text content", # For mixed content
|
|
696
697
|
}
|
|
@@ -705,6 +706,10 @@ XmlText = {
|
|
|
705
706
|
content: "Text content",
|
|
706
707
|
namespace: nil, # or NamespaceClass
|
|
707
708
|
}
|
|
709
|
+
|
|
710
|
+
XmlComment = {
|
|
711
|
+
content: " comment text ", # Content between <!-- and -->
|
|
712
|
+
}
|
|
708
713
|
```
|
|
709
714
|
|
|
710
715
|
**Key Properties**:
|
data/docs/index.adoc
CHANGED
|
@@ -11,13 +11,14 @@ image:https://img.shields.io/github/license/lutaml/lutaml-model.svg[License]
|
|
|
11
11
|
image:https://github.com/lutaml/lutaml-model/actions/workflows/rake.yml/badge.svg["Build", link="https://github.com/lutaml/lutaml-model/actions/workflows/rake.yml"]
|
|
12
12
|
image:https://github.com/lutaml/lutaml-model/actions/workflows/dependent-tests.yml/badge.svg["Dependent tests", link="https://github.com/lutaml/lutaml-model/actions/workflows/dependent-tests.yml"]
|
|
13
13
|
|
|
14
|
-
Lutaml::Model is a Ruby library for creating information models with attributes and types, providing flexible and comprehensive mechanisms for serializing to and from multiple formats including XML, JSON, YAML, TOML, and Hash.
|
|
14
|
+
Lutaml::Model is a Ruby library for creating information models with attributes and types, providing flexible and comprehensive mechanisms for serializing to and from multiple formats including XML, JSON, YAML, TOML, JSON-LD, Turtle, and Hash.
|
|
15
15
|
|
|
16
16
|
== Key features
|
|
17
17
|
|
|
18
18
|
* **Model-driven design** - Define models with attributes and types
|
|
19
|
-
* **Multi-format serialization** - XML, JSON, YAML, TOML, Hash, YAML Stream
|
|
19
|
+
* **Multi-format serialization** - XML, JSON, YAML, TOML, JSON-LD, Turtle, Hash, YAML Stream
|
|
20
20
|
* **XML namespace support** - Full W3C namespace implementation
|
|
21
|
+
* **Linked Data support** - RDF namespaces, JSON-LD, and Turtle serialization
|
|
21
22
|
* **Validation** - Built-in validation with custom rules
|
|
22
23
|
* **Schema generation** - Generate XSD, JSON Schema, YAML Schema
|
|
23
24
|
* **Polymorphism** - Handle multiple types elegantly
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
|
|
5
|
+
module Lutaml
|
|
6
|
+
module JsonLd
|
|
7
|
+
class Adapter < Lutaml::KeyValue::Document
|
|
8
|
+
def self.parse(jsonld_string, _options = {})
|
|
9
|
+
JSON.parse(jsonld_string, create_additions: false)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def to_jsonld(*args)
|
|
13
|
+
options = args.first || {}
|
|
14
|
+
data = @attributes
|
|
15
|
+
if options[:pretty]
|
|
16
|
+
JSON.pretty_generate(data, *args)
|
|
17
|
+
else
|
|
18
|
+
JSON.generate(data, *args)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Lutaml
|
|
4
|
+
module JsonLd
|
|
5
|
+
class Context
|
|
6
|
+
attr_reader :prefixes, :terms
|
|
7
|
+
|
|
8
|
+
def initialize
|
|
9
|
+
@prefixes = {}
|
|
10
|
+
@terms = {}
|
|
11
|
+
@vocab = nil
|
|
12
|
+
@language = nil
|
|
13
|
+
@base = nil
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def prefix(namespace_class)
|
|
17
|
+
@prefixes[namespace_class.prefix] = namespace_class.uri
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def vocab(uri = nil)
|
|
21
|
+
@vocab = uri if uri
|
|
22
|
+
@vocab
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def language(lang = nil)
|
|
26
|
+
@language = lang if lang
|
|
27
|
+
@language
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def base(uri = nil)
|
|
31
|
+
@base = uri if uri
|
|
32
|
+
@base
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def term(name, id: nil, type: nil, container: nil, language: nil,
|
|
36
|
+
reverse: false)
|
|
37
|
+
@terms[name] = TermDefinition.new(
|
|
38
|
+
name: name,
|
|
39
|
+
id: id,
|
|
40
|
+
type: type,
|
|
41
|
+
container: container,
|
|
42
|
+
language: language,
|
|
43
|
+
reverse: reverse,
|
|
44
|
+
)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def to_hash
|
|
48
|
+
ctx = {}
|
|
49
|
+
ctx["@vocab"] = @vocab if @vocab
|
|
50
|
+
ctx["@language"] = @language if @language
|
|
51
|
+
ctx["@base"] = @base if @base
|
|
52
|
+
@prefixes.each { |pfx, uri| ctx[pfx] = uri }
|
|
53
|
+
@terms.each_value { |td| ctx.merge!(td.to_context_hash) }
|
|
54
|
+
ctx
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def resolve(term_name)
|
|
58
|
+
if term_name.include?(":")
|
|
59
|
+
pfx, local = term_name.split(":", 2)
|
|
60
|
+
"#{@prefixes[pfx]}#{local}" if @prefixes.key?(pfx)
|
|
61
|
+
elsif @terms.key?(term_name)
|
|
62
|
+
@terms[term_name].id
|
|
63
|
+
elsif @vocab
|
|
64
|
+
"#{@vocab}#{term_name}"
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Lutaml
|
|
4
|
+
module JsonLd
|
|
5
|
+
class TermDefinition
|
|
6
|
+
attr_reader :name, :id, :type, :container, :language, :reverse
|
|
7
|
+
|
|
8
|
+
def initialize(name:, id: nil, type: nil, container: nil, language: nil,
|
|
9
|
+
reverse: false)
|
|
10
|
+
@name = name
|
|
11
|
+
@id = id
|
|
12
|
+
@type = type
|
|
13
|
+
@container = container
|
|
14
|
+
@language = language
|
|
15
|
+
@reverse = reverse
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def to_context_hash
|
|
19
|
+
if simple_mapping?
|
|
20
|
+
{ @name => @id }
|
|
21
|
+
else
|
|
22
|
+
defn = {}
|
|
23
|
+
defn["@id"] = @id if @id
|
|
24
|
+
defn["@type"] = @type if @type
|
|
25
|
+
defn["@container"] = "@#{@container}" if @container
|
|
26
|
+
defn["@language"] = @language if @language
|
|
27
|
+
defn["@reverse"] = @reverse if @reverse
|
|
28
|
+
{ @name => defn }
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def simple_mapping?
|
|
35
|
+
@id && @type.nil? && @container.nil? && @language.nil? && !@reverse
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|