json-ld 2.2.1 → 3.0.0
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 +5 -5
- data/README.md +55 -55
- data/VERSION +1 -1
- data/lib/json/ld.rb +4 -2
- data/lib/json/ld/api.rb +49 -59
- data/lib/json/ld/compact.rb +60 -56
- data/lib/json/ld/context.rb +52 -40
- data/lib/json/ld/expand.rb +53 -61
- data/lib/json/ld/extensions.rb +31 -16
- data/lib/json/ld/flatten.rb +99 -90
- data/lib/json/ld/format.rb +2 -2
- data/lib/json/ld/frame.rb +47 -30
- data/lib/json/ld/from_rdf.rb +31 -23
- data/lib/json/ld/resource.rb +1 -1
- data/lib/json/ld/to_rdf.rb +4 -2
- data/lib/json/ld/utils.rb +25 -35
- data/lib/json/ld/writer.rb +25 -1
- data/spec/api_spec.rb +1 -0
- data/spec/compact_spec.rb +536 -31
- data/spec/context_spec.rb +109 -43
- data/spec/expand_spec.rb +413 -18
- data/spec/flatten_spec.rb +107 -27
- data/spec/frame_spec.rb +255 -34
- data/spec/from_rdf_spec.rb +102 -3
- data/spec/streaming_writer_spec.rb +8 -9
- data/spec/suite_compact_spec.rb +2 -2
- data/spec/suite_expand_spec.rb +2 -2
- data/spec/suite_flatten_spec.rb +2 -2
- data/spec/suite_frame_spec.rb +2 -2
- data/spec/suite_from_rdf_spec.rb +2 -3
- data/spec/suite_helper.rb +57 -61
- data/spec/suite_remote_doc_spec.rb +2 -2
- data/spec/suite_to_rdf_spec.rb +4 -4
- data/spec/to_rdf_spec.rb +88 -1
- data/spec/writer_spec.rb +5 -6
- metadata +5 -7
- data/spec/suite_error_spec.rb +0 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 33d72fb295b05e075532454ade144d79cf15458946492639f9a941a62668c510
|
4
|
+
data.tar.gz: a382b3bdf08bc1b377d5d01f8d2bae93631e7f9077161fd6bbddfd7477a4c775
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fea935d4ce21a779b4d6eb2bd38eaf8f0859b4e312b8baa509e3325663597cffcef934e3210dda62e339b08d86dc689458fc331b5edf86e500b28ffbe4679548
|
7
|
+
data.tar.gz: 1d589f193efd2725ac4ac4d200de04fea6d13296fa7ad3989cf40cfed30424dd91dfb27ced0eda4ce88d0f5676e90b36e5b4cff8185afac9644fd3cc7edd7c0a
|
data/README.md
CHANGED
@@ -28,13 +28,13 @@ This gem implements an optimized streaming writer used for generating JSON-LD fr
|
|
28
28
|
* RDF Lists are written as separate nodes using `rdf:first` and `rdf:rest` properties.
|
29
29
|
|
30
30
|
## Examples
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
31
|
+
```ruby
|
32
|
+
require 'rubygems'
|
33
|
+
require 'json/ld'
|
34
|
+
```
|
35
35
|
### Expand a Document
|
36
|
-
|
37
|
-
|
36
|
+
```ruby
|
37
|
+
input = JSON.parse %({
|
38
38
|
"@context": {
|
39
39
|
"name": "http://xmlns.com/foaf/0.1/name",
|
40
40
|
"homepage": "http://xmlns.com/foaf/0.1/homepage",
|
@@ -51,9 +51,9 @@ This gem implements an optimized streaming writer used for generating JSON-LD fr
|
|
51
51
|
"http://xmlns.com/foaf/0.1/homepage": [{"@value"=>"http://manu.sporny.org/"}],
|
52
52
|
"http://xmlns.com/foaf/0.1/avatar": [{"@value": "http://twitter.com/account/profile_image/manusporny"}]
|
53
53
|
}]
|
54
|
-
|
54
|
+
```
|
55
55
|
### Compact a Document
|
56
|
-
|
56
|
+
```ruby
|
57
57
|
input = JSON.parse %([{
|
58
58
|
"http://xmlns.com/foaf/0.1/name": ["Manu Sporny"],
|
59
59
|
"http://xmlns.com/foaf/0.1/homepage": [{"@id": "http://manu.sporny.org/"}],
|
@@ -79,9 +79,9 @@ This gem implements an optimized streaming writer used for generating JSON-LD fr
|
|
79
79
|
"homepage": "http://manu.sporny.org/",
|
80
80
|
"name": "Manu Sporny"
|
81
81
|
}
|
82
|
-
|
82
|
+
```
|
83
83
|
### Frame a Document
|
84
|
-
|
84
|
+
```ruby
|
85
85
|
input = JSON.parse %({
|
86
86
|
"@context": {
|
87
87
|
"Book": "http://example.org/vocab#Book",
|
@@ -162,9 +162,9 @@ This gem implements an optimized streaming writer used for generating JSON-LD fr
|
|
162
162
|
}
|
163
163
|
]
|
164
164
|
}
|
165
|
-
|
165
|
+
```
|
166
166
|
### Turn JSON-LD into RDF (Turtle)
|
167
|
-
|
167
|
+
```ruby
|
168
168
|
input = JSON.parse %({
|
169
169
|
"@context": {
|
170
170
|
"": "http://manu.sporny.org/",
|
@@ -185,9 +185,9 @@ This gem implements an optimized streaming writer used for generating JSON-LD fr
|
|
185
185
|
<http://example.org/people#joebob> a foaf:Person;
|
186
186
|
foaf:name "Joe Bob";
|
187
187
|
foaf:nick ("joe" "bob" "jaybe") .
|
188
|
-
|
188
|
+
```
|
189
189
|
### Turn RDF into JSON-LD
|
190
|
-
|
190
|
+
```ruby
|
191
191
|
require 'rdf/turtle'
|
192
192
|
input = RDF::Graph.new << RDF::Turtle::Reader.new(%(
|
193
193
|
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
|
@@ -223,12 +223,12 @@ This gem implements an optimized streaming writer used for generating JSON-LD fr
|
|
223
223
|
"http://xmlns.com/foaf/0.1/name": [{"@value": "Manu Sporny"}]
|
224
224
|
}
|
225
225
|
]
|
226
|
-
|
226
|
+
```
|
227
227
|
## Use a custom Document Loader
|
228
228
|
In some cases, the built-in document loader {JSON::LD::API.documentLoader} is inadequate; for example, when using `http://schema.org` as a remote context, it will be re-loaded every time.
|
229
229
|
|
230
230
|
All entries into the {JSON::LD::API} accept a `:documentLoader` option, which can be used to provide an alternative method to use when loading remote documents. For example:
|
231
|
-
|
231
|
+
```ruby
|
232
232
|
def load_document_local(url, options={}, &block)
|
233
233
|
if RDF::URI(url, canonicalize: true) == RDF::URI('http://schema.org/')
|
234
234
|
remote_document = JSON::LD::API::RemoteDocument.new(url, File.read("etc/schema.org.jsonld"))
|
@@ -237,28 +237,28 @@ All entries into the {JSON::LD::API} accept a `:documentLoader` option, which ca
|
|
237
237
|
JSON::LD::API.documentLoader(url, options, &block)
|
238
238
|
end
|
239
239
|
end
|
240
|
-
|
240
|
+
```
|
241
241
|
Then, when performing something like expansion:
|
242
|
-
|
242
|
+
```ruby
|
243
243
|
JSON::LD::API.expand(input, documentLoader: load_document_local)
|
244
|
-
|
244
|
+
```
|
245
245
|
|
246
246
|
## Preloading contexts
|
247
247
|
In many cases, for small documents, processing time can be dominated by loading and parsing remote contexts. In particular, a small schema.org example may need to download a large context and turn it into an internal representation, before the actual document can be expanded for processing. Using {JSON::LD::Context.add_preloaded}, an implementation can perform this loading up-front, and make it available to the processor.
|
248
|
-
|
248
|
+
```ruby
|
249
249
|
ctx = JSON::LD::Context.new().parse('http://schema.org/')
|
250
250
|
JSON::LD::Context.add_preloaded('http://schema.org/', ctx)
|
251
|
-
|
251
|
+
```
|
252
252
|
On lookup, URIs with an `https` prefix are normalized to `http`.
|
253
253
|
|
254
254
|
A context may be serialized to Ruby to speed this process using `Context#to_rb`. When loaded, this generated file will add entries to the {JSON::LD::Context::PRELOADED}.
|
255
255
|
|
256
256
|
## RDF Reader and Writer
|
257
257
|
{JSON::LD} also acts as a normal RDF reader and writer, using the standard RDF.rb reader/writer interfaces:
|
258
|
-
|
258
|
+
```ruby
|
259
259
|
graph = RDF::Graph.load("etc/doap.jsonld", format: :jsonld)
|
260
260
|
graph.dump(:jsonld, standard_prefixes: true)
|
261
|
-
|
261
|
+
```
|
262
262
|
`RDF::GRAPH#dump` can also take a `:context` option to use a separately defined context
|
263
263
|
|
264
264
|
As JSON-LD may come from many different sources, included as an embedded script tag within an HTML document, the RDF Reader will strip input before the leading `{` or `[` and after the trailing `}` or `]`.
|
@@ -268,7 +268,7 @@ This implementation is being used as a test-bed for features planned for an upco
|
|
268
268
|
|
269
269
|
### Scoped Contexts
|
270
270
|
A term definition can include `@context`, which is applied to values of that object. This is also used when compacting. Taken together, this allows framing to effectively include context definitions more deeply within the framed structure.
|
271
|
-
|
271
|
+
```ruby
|
272
272
|
{
|
273
273
|
"@context": {
|
274
274
|
"ex": "http://example.com/",
|
@@ -283,10 +283,10 @@ A term definition can include `@context`, which is applied to values of that obj
|
|
283
283
|
},
|
284
284
|
"foo": "Bar"
|
285
285
|
}
|
286
|
-
|
286
|
+
```
|
287
287
|
### @id and @type maps
|
288
288
|
The value of `@container` in a term definition can include `@id` or `@type`, in addition to `@set`, `@list`, `@language`, and `@index`. This allows value indexing based on either the `@id` or `@type` of associated objects.
|
289
|
-
|
289
|
+
```ruby
|
290
290
|
{
|
291
291
|
"@context": {
|
292
292
|
"@vocab": "http://example/",
|
@@ -297,10 +297,10 @@ The value of `@container` in a term definition can include `@id` or `@type`, in
|
|
297
297
|
"_:bar": {"label": "Object with @id _:bar"}
|
298
298
|
}
|
299
299
|
}
|
300
|
-
|
300
|
+
```
|
301
301
|
### @graph containers and maps
|
302
302
|
A term can have `@container` set to include `@graph` optionally including `@id` or `@index` and `@set`. In the first form, with `@container` set to `@graph`, the value of a property is treated as a _simple graph object_, meaning that values treated as if they were contained in an object with `@graph`, creating _named graph_ with an anonymous name.
|
303
|
-
|
303
|
+
```ruby
|
304
304
|
{
|
305
305
|
"@context": {
|
306
306
|
"@vocab": "http://example.org/",
|
@@ -310,9 +310,9 @@ A term can have `@container` set to include `@graph` optionally including `@id`
|
|
310
310
|
"value": "x"
|
311
311
|
}
|
312
312
|
}
|
313
|
-
|
313
|
+
```
|
314
314
|
which expands to the following:
|
315
|
-
|
315
|
+
```ruby
|
316
316
|
[{
|
317
317
|
"http://example.org/input": [{
|
318
318
|
"@graph": [{
|
@@ -320,18 +320,18 @@ which expands to the following:
|
|
320
320
|
}]
|
321
321
|
}]
|
322
322
|
}]
|
323
|
-
|
323
|
+
```
|
324
324
|
Compaction reverses this process, optionally ensuring that a single value is contained within an array of `@container` also includes `@set`:
|
325
|
-
|
325
|
+
```ruby
|
326
326
|
{
|
327
327
|
"@context": {
|
328
328
|
"@vocab": "http://example.org/",
|
329
329
|
"input": {"@container": ["@graph", "@set"]}
|
330
330
|
}
|
331
331
|
}
|
332
|
-
|
332
|
+
```
|
333
333
|
A graph map uses the map form already existing for `@index`, `@language`, `@type`, and `@id` where the index is either an index value or an id.
|
334
|
-
|
334
|
+
```ruby
|
335
335
|
{
|
336
336
|
"@context": {
|
337
337
|
"@vocab": "http://example.org/",
|
@@ -341,9 +341,9 @@ A graph map uses the map form already existing for `@index`, `@language`, `@type
|
|
341
341
|
"g1": {"value": "x"}
|
342
342
|
}
|
343
343
|
}
|
344
|
-
|
344
|
+
```
|
345
345
|
treats "g1" as an index, and expands to the following:
|
346
|
-
|
346
|
+
```ruby
|
347
347
|
[{
|
348
348
|
"http://example.org/input": [{
|
349
349
|
"@index": "g1",
|
@@ -352,11 +352,11 @@ treats "g1" as an index, and expands to the following:
|
|
352
352
|
}]
|
353
353
|
}]
|
354
354
|
}])
|
355
|
-
|
355
|
+
```
|
356
356
|
This can also include `@set` to ensure that, when compacting, a single value of an index will be in array form.
|
357
357
|
|
358
358
|
The _id_ version is similar:
|
359
|
-
|
359
|
+
```ruby
|
360
360
|
{
|
361
361
|
"@context": {
|
362
362
|
"@vocab": "http://example.org/",
|
@@ -366,9 +366,9 @@ The _id_ version is similar:
|
|
366
366
|
"http://example.com/g1": {"value": "x"}
|
367
367
|
}
|
368
368
|
}
|
369
|
-
|
369
|
+
```
|
370
370
|
which expands to:
|
371
|
-
|
371
|
+
```ruby
|
372
372
|
[{
|
373
373
|
"http://example.org/input": [{
|
374
374
|
"@id": "http://example.com/g1",
|
@@ -377,10 +377,10 @@ which expands to:
|
|
377
377
|
}]
|
378
378
|
}]
|
379
379
|
}])
|
380
|
-
|
380
|
+
```
|
381
381
|
### Transparent Nesting
|
382
382
|
Many JSON APIs separate properties from their entities using an intermediate object. For example, a set of possible labels may be grouped under a common property:
|
383
|
-
|
383
|
+
```json
|
384
384
|
{
|
385
385
|
"@context": {
|
386
386
|
"skos": "http://www.w3.org/2004/02/skos/core#",
|
@@ -396,9 +396,9 @@ Many JSON APIs separate properties from their entities using an intermediate obj
|
|
396
396
|
"other_label": "This is the other label"
|
397
397
|
}
|
398
398
|
}
|
399
|
-
|
399
|
+
```
|
400
400
|
In this case, the `labels` property is semantically meaningless. Defining it as equivalent to `@nest` causes it to be ignored when expanding, making it equivalent to the following:
|
401
|
-
|
401
|
+
```json
|
402
402
|
{
|
403
403
|
"@context": {
|
404
404
|
"skos": "http://www.w3.org/2004/02/skos/core#",
|
@@ -412,9 +412,9 @@ Many JSON APIs separate properties from their entities using an intermediate obj
|
|
412
412
|
"main_label": "This is the main label for my resource",
|
413
413
|
"other_label": "This is the other label"
|
414
414
|
}
|
415
|
-
|
415
|
+
```
|
416
416
|
Similarly, properties may be marked with "@nest": "nest-term", to cause them to be nested. Note that the `@nest` keyword can also be aliased in the context.
|
417
|
-
|
417
|
+
```json
|
418
418
|
{
|
419
419
|
"@context": {
|
420
420
|
"skos": "http://www.w3.org/2004/02/skos/core#",
|
@@ -430,7 +430,7 @@ Many JSON APIs separate properties from their entities using an intermediate obj
|
|
430
430
|
"other_label": "This is the other label"
|
431
431
|
}
|
432
432
|
}
|
433
|
-
|
433
|
+
```
|
434
434
|
In this way, nesting survives round-tripping through expansion, and framed output can include nested properties.
|
435
435
|
|
436
436
|
### Framing Updates
|
@@ -469,20 +469,20 @@ Note, the API method signatures differed in versions before 1.0, in that they al
|
|
469
469
|
|
470
470
|
## Dependencies
|
471
471
|
* [Ruby](http://ruby-lang.org/) (>= 2.2.2)
|
472
|
-
* [RDF.rb](http://rubygems.org/gems/rdf) (
|
473
|
-
* [JSON](https://rubygems.org/gems/json) (>= 1
|
472
|
+
* [RDF.rb](http://rubygems.org/gems/rdf) (~> 3.0)
|
473
|
+
* [JSON](https://rubygems.org/gems/json) (>= 2.1)
|
474
474
|
|
475
475
|
## Installation
|
476
476
|
The recommended installation method is via [RubyGems](http://rubygems.org/).
|
477
477
|
To install the latest official release of the `JSON-LD` gem, do:
|
478
|
-
|
479
|
-
|
480
|
-
|
478
|
+
```bash
|
479
|
+
% [sudo] gem install json-ld
|
480
|
+
```
|
481
481
|
## Download
|
482
482
|
To get a local working copy of the development repository, do:
|
483
|
-
|
484
|
-
|
485
|
-
|
483
|
+
```bash
|
484
|
+
% git clone git://github.com/ruby-rdf/json-ld.git
|
485
|
+
```
|
486
486
|
## Mailing List
|
487
487
|
* <http://lists.w3.org/Archives/Public/public-rdf-ruby/>
|
488
488
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
3.0.0
|
data/lib/json/ld.rb
CHANGED
@@ -55,6 +55,7 @@ module JSON
|
|
55
55
|
@language
|
56
56
|
@list
|
57
57
|
@nest
|
58
|
+
@none
|
58
59
|
@omitDefault
|
59
60
|
@requireAll
|
60
61
|
@reverse
|
@@ -90,6 +91,8 @@ module JSON
|
|
90
91
|
array_nl: "\n"
|
91
92
|
)
|
92
93
|
|
94
|
+
MAX_CONTEXTS_LOADED = 50
|
95
|
+
|
93
96
|
class JsonLdError < StandardError
|
94
97
|
def to_s
|
95
98
|
"#{self.class.instance_variable_get :@code}: #{super}"
|
@@ -99,7 +102,6 @@ module JSON
|
|
99
102
|
end
|
100
103
|
|
101
104
|
class CollidingKeywords < JsonLdError; @code = "colliding keywords"; end
|
102
|
-
class CompactionToListOfLists < JsonLdError; @code = "compaction to list of lists"; end
|
103
105
|
class ConflictingIndexes < JsonLdError; @code = "conflicting indexes"; end
|
104
106
|
class CyclicIRIMapping < JsonLdError; @code = "cyclic IRI mapping"; end
|
105
107
|
class InvalidBaseIRI < JsonLdError; @code = "invalid base IRI"; end
|
@@ -132,9 +134,9 @@ module JSON
|
|
132
134
|
class InvalidValueObjectValue < JsonLdError; @code = "invalid value object value"; end
|
133
135
|
class InvalidVocabMapping < JsonLdError; @code = "invalid vocab mapping"; end
|
134
136
|
class KeywordRedefinition < JsonLdError; @code = "keyword redefinition"; end
|
135
|
-
class ListOfLists < JsonLdError; @code = "list of lists"; end
|
136
137
|
class LoadingDocumentFailed < JsonLdError; @code = "loading document failed"; end
|
137
138
|
class LoadingRemoteContextFailed < JsonLdError; @code = "loading remote context failed"; end
|
139
|
+
class ContextOverflow < JsonLdError; @code = "maximum number of @context URLs exceeded"; end
|
138
140
|
class MultipleContextLinkHeaders < JsonLdError; @code = "multiple context link headers"; end
|
139
141
|
class ProcessingModeConflict < JsonLdError; @code = "processing mode conflict"; end
|
140
142
|
class RecursiveContextInclusion < JsonLdError; @code = "recursive context inclusion"; end
|
data/lib/json/ld/api.rb
CHANGED
@@ -72,10 +72,7 @@ module JSON::LD
|
|
72
72
|
# @option options [Boolean, String, RDF::URI] :flatten
|
73
73
|
# If set to a value that is not `false`, the JSON-LD processor must modify the output of the Compaction Algorithm or the Expansion Algorithm by coalescing all properties associated with each subject via the Flattening Algorithm. The value of `flatten must` be either an _IRI_ value representing the name of the graph to flatten, or `true`. If the value is `true`, then the first graph encountered in the input document is selected and flattened.
|
74
74
|
# @option options [String] :processingMode
|
75
|
-
# Processing mode, json-ld-1.0 or json-ld-1.1.
|
76
|
-
#
|
77
|
-
# * json-ld-1.1-expand-frame – special frame expansion mode.
|
78
|
-
#
|
75
|
+
# Processing mode, json-ld-1.0 or json-ld-1.1.
|
79
76
|
# If `processingMode` is not specified, a mode of `json-ld-1.0` or `json-ld-1.1` is set, the context used for `expansion` or `compaction`.
|
80
77
|
# @option options [Boolean] :rename_bnodes (true)
|
81
78
|
# Rename bnodes as part of expansion, or keep them the same.
|
@@ -86,16 +83,15 @@ module JSON::LD
|
|
86
83
|
# @yield [api]
|
87
84
|
# @yieldparam [API]
|
88
85
|
# @raise [JsonLdError]
|
89
|
-
def initialize(input, context,
|
86
|
+
def initialize(input, context, rename_bnodes: true, unique_bnodes: false, **options, &block)
|
90
87
|
@options = {
|
91
88
|
compactArrays: true,
|
92
|
-
rename_bnodes: true,
|
93
89
|
documentLoader: self.class.method(:documentLoader)
|
94
90
|
}.merge(options)
|
95
|
-
@namer =
|
91
|
+
@namer = unique_bnodes ? BlankNodeUniqer.new : (rename_bnodes ? BlankNodeNamer.new("b") : BlankNodeMapper.new)
|
96
92
|
|
97
93
|
# For context via Link header
|
98
|
-
|
94
|
+
_, context_ref = nil, nil
|
99
95
|
|
100
96
|
@value = case input
|
101
97
|
when Array, Hash then input.dup
|
@@ -115,7 +111,6 @@ module JSON::LD
|
|
115
111
|
when String
|
116
112
|
remote_doc = @options[:documentLoader].call(input, @options)
|
117
113
|
|
118
|
-
remote_base = remote_doc.documentUrl
|
119
114
|
context_ref = remote_doc.contextUrl
|
120
115
|
@options = {base: remote_doc.documentUrl}.merge(@options) unless @options[:no_default_base]
|
121
116
|
|
@@ -166,10 +161,12 @@ module JSON::LD
|
|
166
161
|
# @return [Object, Array<Hash>]
|
167
162
|
# If a block is given, the result of evaluating the block is returned, otherwise, the expanded JSON-LD document
|
168
163
|
# @see http://json-ld.org/spec/latest/json-ld-api/#expansion-algorithm
|
169
|
-
def self.expand(input,
|
164
|
+
def self.expand(input, ordered: true, framing: false, **options, &block)
|
170
165
|
result, doc_base = nil
|
171
166
|
API.new(input, options[:expandContext], options) do
|
172
|
-
result = self.expand(self.value, nil, self.context,
|
167
|
+
result = self.expand(self.value, nil, self.context,
|
168
|
+
ordered: ordered,
|
169
|
+
framing: framing)
|
173
170
|
doc_base = @options[:base]
|
174
171
|
end
|
175
172
|
|
@@ -213,18 +210,18 @@ module JSON::LD
|
|
213
210
|
# If a block is given, the result of evaluating the block is returned, otherwise, the compacted JSON-LD document
|
214
211
|
# @raise [JsonLdError]
|
215
212
|
# @see http://json-ld.org/spec/latest/json-ld-api/#compaction-algorithm
|
216
|
-
def self.compact(input, context,
|
213
|
+
def self.compact(input, context, expanded: false, **options)
|
217
214
|
result = nil
|
218
215
|
options = {compactToRelative: true}.merge(options)
|
219
216
|
|
220
217
|
# 1) Perform the Expansion Algorithm on the JSON-LD input.
|
221
218
|
# This removes any existing context to allow the given context to be cleanly applied.
|
222
|
-
expanded_input =
|
219
|
+
expanded_input = expanded ? input : API.expand(input, options) do |res, base_iri|
|
223
220
|
options[:base] ||= base_iri if options[:compactToRelative]
|
224
|
-
|
221
|
+
res
|
225
222
|
end
|
226
223
|
|
227
|
-
API.new(expanded_input, context,
|
224
|
+
API.new(expanded_input, context, no_default_base: true, **options) do
|
228
225
|
log_debug(".compact") {"expanded input: #{expanded_input.to_json(JSON_STATE) rescue 'malformed json'}"}
|
229
226
|
result = compact(value)
|
230
227
|
|
@@ -258,18 +255,18 @@ module JSON::LD
|
|
258
255
|
# @return [Object, Hash]
|
259
256
|
# If a block is given, the result of evaluating the block is returned, otherwise, the flattened JSON-LD document
|
260
257
|
# @see http://json-ld.org/spec/latest/json-ld-api/#framing-algorithm
|
261
|
-
def self.flatten(input, context,
|
258
|
+
def self.flatten(input, context, expanded: false, **options)
|
262
259
|
flattened = []
|
263
260
|
options = {compactToRelative: true}.merge(options)
|
264
261
|
|
265
262
|
# Expand input to simplify processing
|
266
|
-
expanded_input =
|
263
|
+
expanded_input = expanded ? input : API.expand(input, options) do |result, base_iri|
|
267
264
|
options[:base] ||= base_iri if options[:compactToRelative]
|
268
265
|
result
|
269
266
|
end
|
270
267
|
|
271
268
|
# Initialize input using
|
272
|
-
API.new(expanded_input, context,
|
269
|
+
API.new(expanded_input, context, no_default_base: true, **options) do
|
273
270
|
log_debug(".flatten") {"expanded input: #{value.to_json(JSON_STATE) rescue 'malformed json'}"}
|
274
271
|
|
275
272
|
# Initialize node map to a JSON object consisting of a single member whose key is @default and whose value is an empty JSON object.
|
@@ -277,24 +274,23 @@ module JSON::LD
|
|
277
274
|
create_node_map(value, graph_maps)
|
278
275
|
|
279
276
|
default_graph = graph_maps['@default']
|
280
|
-
graph_maps.keys.
|
277
|
+
graph_maps.keys.sort.each do |graph_name|
|
281
278
|
next if graph_name == '@default'
|
282
279
|
|
283
280
|
graph = graph_maps[graph_name]
|
284
281
|
entry = default_graph[graph_name] ||= {'@id' => graph_name}
|
285
282
|
nodes = entry['@graph'] ||= []
|
286
|
-
graph.keys.
|
283
|
+
graph.keys.sort.each do |id|
|
287
284
|
nodes << graph[id] unless node_reference?(graph[id])
|
288
285
|
end
|
289
286
|
end
|
290
|
-
default_graph.keys.
|
287
|
+
default_graph.keys.sort.each do |id|
|
291
288
|
flattened << default_graph[id] unless node_reference?(default_graph[id])
|
292
289
|
end
|
293
290
|
|
294
291
|
if context && !flattened.empty?
|
295
292
|
# Otherwise, return the result of compacting flattened according the Compaction algorithm passing context ensuring that the compaction result uses the @graph keyword (or its alias) at the top-level, even if the context is empty or if there is only one element to put in the @graph array. This ensures that the returned document has a deterministic structure.
|
296
|
-
compacted = compact(flattened)
|
297
|
-
compacted = [compacted] unless compacted.is_a?(Array)
|
293
|
+
compacted = as_array(compact(flattened))
|
298
294
|
kwgraph = self.context.compact_iri('@graph', quiet: true)
|
299
295
|
flattened = self.context.serialize.merge(kwgraph => compacted)
|
300
296
|
end
|
@@ -322,7 +318,7 @@ module JSON::LD
|
|
322
318
|
# @option options [Boolean] :omitDefault (false)
|
323
319
|
# a flag specifying that properties that are missing from the JSON-LD input should be omitted from the output.
|
324
320
|
# @option options [Boolean] :expanded Input is already expanded
|
325
|
-
# @option options [Boolean] :
|
321
|
+
# @option options [Boolean] :omitGraph does not use `@graph` at top level unless necessary to describe multiple objects, defaults to `true` if processingMode is 1.1, otherwise `false`.
|
326
322
|
# @yield jsonld
|
327
323
|
# @yieldparam [Hash] jsonld
|
328
324
|
# The framed JSON-LD document
|
@@ -331,7 +327,7 @@ module JSON::LD
|
|
331
327
|
# If a block is given, the result of evaluating the block is returned, otherwise, the framed JSON-LD document
|
332
328
|
# @raise [InvalidFrame]
|
333
329
|
# @see http://json-ld.org/spec/latest/json-ld-api/#framing-algorithm
|
334
|
-
def self.frame(input, frame,
|
330
|
+
def self.frame(input, frame, expanded: false, **options)
|
335
331
|
result = nil
|
336
332
|
options = {
|
337
333
|
base: (input if input.is_a?(String)),
|
@@ -341,7 +337,7 @@ module JSON::LD
|
|
341
337
|
explicit: false,
|
342
338
|
requireAll: true,
|
343
339
|
omitDefault: false,
|
344
|
-
|
340
|
+
omitGraph: false,
|
345
341
|
documentLoader: method(:documentLoader)
|
346
342
|
}.merge(options)
|
347
343
|
|
@@ -365,20 +361,21 @@ module JSON::LD
|
|
365
361
|
end
|
366
362
|
|
367
363
|
# Expand input to simplify processing
|
368
|
-
expanded_input =
|
364
|
+
expanded_input = expanded ? input : API.expand(input, options) do |res, base_iri|
|
369
365
|
options[:base] ||= base_iri if options[:compactToRelative]
|
370
|
-
|
366
|
+
res
|
371
367
|
end
|
372
368
|
|
373
369
|
# Expand frame to simplify processing
|
374
|
-
expanded_frame = API.expand(frame,
|
370
|
+
expanded_frame = API.expand(frame, framing: true, **options)
|
375
371
|
|
376
372
|
# Initialize input using frame as context
|
377
|
-
API.new(expanded_input,
|
373
|
+
API.new(expanded_input, frame['@context'], no_default_base: true, **options) do
|
374
|
+
log_debug(".frame") {"expanded input: #{expanded_input.to_json(JSON_STATE) rescue 'malformed json'}"}
|
378
375
|
log_debug(".frame") {"expanded frame: #{expanded_frame.to_json(JSON_STATE) rescue 'malformed json'}"}
|
379
376
|
|
380
377
|
# Get framing nodes from expanded input, replacing Blank Node identifiers as necessary
|
381
|
-
create_node_map(value, framing_state[:graphMap],
|
378
|
+
create_node_map(value, framing_state[:graphMap], active_graph: '@default')
|
382
379
|
|
383
380
|
frame_keys = frame.keys.map {|k| context.expand_iri(k, vocab: true, quiet: true)}
|
384
381
|
if frame_keys.include?('@graph')
|
@@ -394,24 +391,29 @@ module JSON::LD
|
|
394
391
|
framing_state[:subjects] = framing_state[:graphMap][framing_state[:graph]]
|
395
392
|
|
396
393
|
result = []
|
397
|
-
frame(framing_state, framing_state[:subjects].keys.sort, (expanded_frame.first || {}),
|
394
|
+
frame(framing_state, framing_state[:subjects].keys.sort, (expanded_frame.first || {}), parent: result, **options)
|
398
395
|
|
399
396
|
# Count blank node identifiers used in the document, if pruning
|
400
|
-
|
401
|
-
count_blank_node_identifiers(result).collect {|k, v| k if v == 1}.compact
|
397
|
+
unless @options[:processingMode] == 'json-ld-1.0'
|
398
|
+
bnodes_to_clear = count_blank_node_identifiers(result).collect {|k, v| k if v == 1}.compact
|
399
|
+
result = prune_bnodes(result, bnodes_to_clear)
|
402
400
|
end
|
403
401
|
|
404
402
|
# Initalize context from frame
|
405
403
|
@context = @context.parse(frame['@context'])
|
406
404
|
# Compact result
|
407
405
|
compacted = compact(result)
|
408
|
-
compacted = [compacted] unless compacted.is_a?(Array)
|
406
|
+
compacted = [compacted] unless options[:omitGraph] || compacted.is_a?(Array)
|
409
407
|
|
410
408
|
# Add the given context to the output
|
411
|
-
|
412
|
-
|
409
|
+
result = if !compacted.is_a?(Array)
|
410
|
+
context.serialize.merge(compacted)
|
411
|
+
else
|
412
|
+
kwgraph = context.compact_iri('@graph', quiet: true)
|
413
|
+
context.serialize.merge({kwgraph => compacted})
|
414
|
+
end
|
413
415
|
log_debug(".frame") {"after compact: #{result.to_json(JSON_STATE) rescue 'malformed json'}"}
|
414
|
-
result = cleanup_preserve(result
|
416
|
+
result = cleanup_preserve(result)
|
415
417
|
end
|
416
418
|
|
417
419
|
block_given? ? yield(result) : result
|
@@ -430,7 +432,7 @@ module JSON::LD
|
|
430
432
|
# @yield statement
|
431
433
|
# @yieldparam [RDF::Statement] statement
|
432
434
|
# @return [RDF::Enumerable] set of statements, unless a block is given.
|
433
|
-
def self.toRdf(input,
|
435
|
+
def self.toRdf(input, expanded: false, **options, &block)
|
434
436
|
unless block_given?
|
435
437
|
results = []
|
436
438
|
results.extend(RDF::Enumerable)
|
@@ -441,7 +443,7 @@ module JSON::LD
|
|
441
443
|
end
|
442
444
|
|
443
445
|
# Expand input to simplify processing
|
444
|
-
expanded_input =
|
446
|
+
expanded_input = expanded ? input : API.expand(input, ordered: false, **options)
|
445
447
|
|
446
448
|
API.new(expanded_input, nil, options) do
|
447
449
|
# 1) Perform the Expansion Algorithm on the JSON-LD input.
|
@@ -453,19 +455,9 @@ module JSON::LD
|
|
453
455
|
item_to_rdf(node) do |statement|
|
454
456
|
next if statement.predicate.node? && !options[:produceGeneralizedRdf]
|
455
457
|
|
456
|
-
# Drop
|
457
|
-
|
458
|
-
|
459
|
-
when RDF::URI
|
460
|
-
r.relative?
|
461
|
-
when RDF::Literal
|
462
|
-
r.has_datatype? && r.datatype.relative?
|
463
|
-
else
|
464
|
-
false
|
465
|
-
end
|
466
|
-
end
|
467
|
-
if relative
|
468
|
-
log_debug(".toRdf") {"drop statement with relative IRIs: #{statement.to_ntriples}"}
|
458
|
+
# Drop invalid statements (other than IRIs)
|
459
|
+
unless statement.valid_extended?
|
460
|
+
log_debug(".toRdf") {"drop invalid statement: #{statement.to_nquads}"}
|
469
461
|
next
|
470
462
|
end
|
471
463
|
|
@@ -480,7 +472,7 @@ module JSON::LD
|
|
480
472
|
#
|
481
473
|
# The resulting `Array` is either returned or yielded, if a block is given.
|
482
474
|
#
|
483
|
-
# @param [
|
475
|
+
# @param [RDF::Enumerable] input
|
484
476
|
# @param [Hash{Symbol => Object}] options
|
485
477
|
# @option options (see #initialize)
|
486
478
|
# @option options [Boolean] :useRdfType (false)
|
@@ -492,9 +484,7 @@ module JSON::LD
|
|
492
484
|
# @yieldreturn [Object] returned object
|
493
485
|
# @return [Object, Hash]
|
494
486
|
# If a block is given, the result of evaluating the block is returned, otherwise, the expanded JSON-LD document
|
495
|
-
def self.fromRdf(input,
|
496
|
-
useRdfType = options.fetch(:useRdfType, false)
|
497
|
-
useNativeTypes = options.fetch(:useNativeTypes, false)
|
487
|
+
def self.fromRdf(input, useRdfType: false, useNativeTypes: false, **options, &block)
|
498
488
|
result = nil
|
499
489
|
|
500
490
|
API.new(nil, nil, options) do |api|
|
@@ -516,12 +506,12 @@ module JSON::LD
|
|
516
506
|
# @return [Object, RemoteDocument]
|
517
507
|
# If a block is given, the result of evaluating the block is returned, otherwise, the retrieved remote document and context information unless block given
|
518
508
|
# @raise [JsonLdError]
|
519
|
-
def self.documentLoader(url,
|
509
|
+
def self.documentLoader(url, validate: false, **options)
|
520
510
|
options = OPEN_OPTS.merge(options)
|
521
511
|
RDF::Util::File.open_file(url, options) do |remote_doc|
|
522
512
|
content_type = remote_doc.content_type if remote_doc.respond_to?(:content_type)
|
523
513
|
# If the passed input is a DOMString representing the IRI of a remote document, dereference it. If the retrieved document's content type is neither application/json, nor application/ld+json, nor any other media type using a +json suffix as defined in [RFC6839], reject the promise passing an loading document failed error.
|
524
|
-
if content_type &&
|
514
|
+
if content_type && validate
|
525
515
|
main, sub = content_type.split("/")
|
526
516
|
raise JSON::LD::JsonLdError::LoadingDocumentFailed, "url: #{url}, content_type: #{content_type}" if
|
527
517
|
main != 'application' ||
|