json-ld 2.2.1 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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' ||
|