json-ld 3.1.3 → 3.1.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +138 -49
- data/VERSION +1 -1
- data/bin/jsonld +28 -31
- data/lib/json/ld.rb +8 -2
- data/lib/json/ld/api.rb +55 -38
- data/lib/json/ld/compact.rb +68 -40
- data/lib/json/ld/conneg.rb +1 -1
- data/lib/json/ld/context.rb +570 -521
- data/lib/json/ld/expand.rb +203 -84
- data/lib/json/ld/extensions.rb +4 -4
- data/lib/json/ld/flatten.rb +92 -9
- data/lib/json/ld/format.rb +21 -8
- data/lib/json/ld/frame.rb +8 -8
- data/lib/json/ld/from_rdf.rb +42 -19
- data/lib/json/ld/reader.rb +21 -11
- data/lib/json/ld/streaming_reader.rb +578 -0
- data/lib/json/ld/streaming_writer.rb +4 -4
- data/lib/json/ld/to_rdf.rb +11 -7
- data/lib/json/ld/utils.rb +13 -13
- data/lib/json/ld/writer.rb +12 -5
- data/spec/api_spec.rb +1 -1
- data/spec/compact_spec.rb +207 -3
- data/spec/context_spec.rb +4 -42
- data/spec/expand_spec.rb +631 -0
- data/spec/flatten_spec.rb +517 -1
- data/spec/from_rdf_spec.rb +181 -0
- data/spec/matchers.rb +1 -1
- data/spec/rdfstar_spec.rb +25 -0
- data/spec/reader_spec.rb +33 -34
- data/spec/spec_helper.rb +33 -0
- data/spec/streaming_reader_spec.rb +237 -0
- data/spec/suite_flatten_spec.rb +4 -0
- data/spec/suite_frame_spec.rb +7 -0
- data/spec/suite_helper.rb +25 -13
- data/spec/suite_to_rdf_spec.rb +1 -0
- data/spec/to_rdf_spec.rb +209 -3
- data/spec/writer_spec.rb +193 -0
- metadata +68 -63
data/lib/json/ld.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
2
|
# frozen_string_literal: true
|
3
3
|
$:.unshift(File.expand_path("../ld", __FILE__))
|
4
|
-
require 'rdf' # @see
|
4
|
+
require 'rdf' # @see https://rubygems.org/gems/rdf
|
5
5
|
require 'multi_json'
|
6
6
|
require 'set'
|
7
7
|
|
@@ -19,7 +19,7 @@ module JSON
|
|
19
19
|
# end
|
20
20
|
# end
|
21
21
|
#
|
22
|
-
# @see
|
22
|
+
# @see https://rubygems.org/gems/rdf
|
23
23
|
# @see http://www.w3.org/TR/REC-rdf-syntax/
|
24
24
|
#
|
25
25
|
# @author [Gregg Kellogg](http://greggkellogg.net/)
|
@@ -34,6 +34,8 @@ module JSON
|
|
34
34
|
autoload :Normalize, 'json/ld/normalize'
|
35
35
|
autoload :Reader, 'json/ld/reader'
|
36
36
|
autoload :Resource, 'json/ld/resource'
|
37
|
+
autoload :StreamingReader, 'json/ld/streaming_reader'
|
38
|
+
autoload :StreamingWriter, 'json/ld/streaming_writer'
|
37
39
|
autoload :VERSION, 'json/ld/version'
|
38
40
|
autoload :Writer, 'json/ld/writer'
|
39
41
|
|
@@ -45,6 +47,7 @@ module JSON
|
|
45
47
|
DEFAULT_CONTEXT = "http://schema.org"
|
46
48
|
|
47
49
|
KEYWORDS = Set.new(%w(
|
50
|
+
@annotation
|
48
51
|
@base
|
49
52
|
@container
|
50
53
|
@context
|
@@ -114,6 +117,7 @@ module JSON
|
|
114
117
|
class CollidingKeywords < JsonLdError; @code = "colliding keywords"; end
|
115
118
|
class ConflictingIndexes < JsonLdError; @code = "conflicting indexes"; end
|
116
119
|
class CyclicIRIMapping < JsonLdError; @code = "cyclic IRI mapping"; end
|
120
|
+
class InvalidAnnotation < JsonLdError; @code = "invalid annotation"; end
|
117
121
|
class InvalidBaseIRI < JsonLdError; @code = "invalid base IRI"; end
|
118
122
|
class InvalidContainerMapping < JsonLdError; @code = "invalid container mapping"; end
|
119
123
|
class InvalidContextEntry < JsonLdError; @code = "invalid context entry"; end
|
@@ -135,6 +139,7 @@ module JSON
|
|
135
139
|
class InvalidNestValue < JsonLdError; @code = "invalid @nest value"; end
|
136
140
|
class InvalidPrefixValue < JsonLdError; @code = "invalid @prefix value"; end
|
137
141
|
class InvalidPropagateValue < JsonLdError; @code = "invalid @propagate value"; end
|
142
|
+
class InvalidEmbeddedNode < JsonLdError; @code = "invalid embedded node"; end
|
138
143
|
class InvalidRemoteContext < JsonLdError; @code = "invalid remote context"; end
|
139
144
|
class InvalidReverseProperty < JsonLdError; @code = "invalid reverse property"; end
|
140
145
|
class InvalidReversePropertyMap < JsonLdError; @code = "invalid reverse property map"; end
|
@@ -143,6 +148,7 @@ module JSON
|
|
143
148
|
class InvalidScopedContext < JsonLdError; @code = "invalid scoped context"; end
|
144
149
|
class InvalidScriptElement < JsonLdError; @code = "invalid script element"; end
|
145
150
|
class InvalidSetOrListObject < JsonLdError; @code = "invalid set or list object"; end
|
151
|
+
class InvalidStreamingKeyOrder < JsonLdError; @code = 'invalid streaming key order' end
|
146
152
|
class InvalidTermDefinition < JsonLdError; @code = "invalid term definition"; end
|
147
153
|
class InvalidBaseDirection < JsonLdError; @code = "invalid base direction"; end
|
148
154
|
class InvalidTypedValue < JsonLdError; @code = "invalid typed value"; end
|
data/lib/json/ld/api.rb
CHANGED
@@ -66,7 +66,7 @@ module JSON::LD
|
|
66
66
|
# @param [String, #read, Hash, Array, JSON::LD::Context] context
|
67
67
|
# An external context to use additionally to the context embedded in input when expanding the input.
|
68
68
|
# @param [Hash{Symbol => Object}] options
|
69
|
-
# @option options [String, #to_s] :base
|
69
|
+
# @option options [RDF::URI, String, #to_s] :base
|
70
70
|
# The Base IRI to use when expanding the document. This overrides the value of `input` if it is a _IRI_. If not specified and `input` is not an _IRI_, the base IRI defaults to the current document IRI if in a browser context, or the empty string if there is no document context. If not specified, and a base IRI is found from `input`, options[:base] will be modified with this value.
|
71
71
|
# @option options [Boolean] :compactArrays (true)
|
72
72
|
# If set to `true`, the JSON-LD processor replaces arrays with just one element with that element during compaction. If set to `false`, all arrays will remain arrays even if they have just one element.
|
@@ -89,6 +89,8 @@ module JSON::LD
|
|
89
89
|
# @option options [String] :processingMode
|
90
90
|
# Processing mode, json-ld-1.0 or json-ld-1.1.
|
91
91
|
# 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`.
|
92
|
+
# @option options [Boolean] rdfstar (false)
|
93
|
+
# support parsing JSON-LD* statement resources.
|
92
94
|
# @option options [Boolean] :rename_bnodes (true)
|
93
95
|
# Rename bnodes as part of expansion, or keep them the same.
|
94
96
|
# @option options [Boolean] :unique_bnodes (false)
|
@@ -100,14 +102,17 @@ module JSON::LD
|
|
100
102
|
# @yield [api]
|
101
103
|
# @yieldparam [API]
|
102
104
|
# @raise [JsonLdError]
|
103
|
-
def initialize(input, context,
|
105
|
+
def initialize(input, context, **options, &block)
|
104
106
|
@options = {
|
105
107
|
compactArrays: true,
|
106
108
|
ordered: false,
|
107
109
|
extractAllScripts: false,
|
110
|
+
rename_bnodes: true,
|
111
|
+
unique_bnodes: false,
|
108
112
|
}.merge(options)
|
109
|
-
@namer = unique_bnodes ? BlankNodeUniqer.new : (rename_bnodes ? BlankNodeNamer.new("b") : BlankNodeMapper.new)
|
113
|
+
@namer = @options[:unique_bnodes] ? BlankNodeUniqer.new : (@options[:rename_bnodes] ? BlankNodeNamer.new("b") : BlankNodeMapper.new)
|
110
114
|
|
115
|
+
@options[:base] = RDF::URI(@options[:base]) if @options[:base] && !@options[:base].is_a?(RDF::URI)
|
111
116
|
# For context via Link header
|
112
117
|
_, context_ref = nil, nil
|
113
118
|
|
@@ -117,7 +122,7 @@ module JSON::LD
|
|
117
122
|
remote_doc = self.class.loadRemoteDocument(input, **@options)
|
118
123
|
|
119
124
|
context_ref = remote_doc.contextUrl
|
120
|
-
@options[:base] = remote_doc.documentUrl if remote_doc.documentUrl && !@options[:no_default_base]
|
125
|
+
@options[:base] = RDF::URI(remote_doc.documentUrl) if remote_doc.documentUrl && !@options[:no_default_base]
|
121
126
|
|
122
127
|
case remote_doc.document
|
123
128
|
when String
|
@@ -130,7 +135,7 @@ module JSON::LD
|
|
130
135
|
|
131
136
|
# If not provided, first use context from document, or from a Link header
|
132
137
|
context ||= context_ref || {}
|
133
|
-
@context = Context.parse(context
|
138
|
+
@context = Context.parse(context, **@options)
|
134
139
|
|
135
140
|
if block_given?
|
136
141
|
case block.arity
|
@@ -163,10 +168,9 @@ module JSON::LD
|
|
163
168
|
# If a block is given, the result of evaluating the block is returned, otherwise, the expanded JSON-LD document
|
164
169
|
# @see https://www.w3.org/TR/json-ld11-api/#expansion-algorithm
|
165
170
|
def self.expand(input, framing: false, **options, &block)
|
166
|
-
result
|
171
|
+
result = doc_base = nil
|
167
172
|
API.new(input, options[:expandContext], **options) do
|
168
173
|
result = self.expand(self.value, nil, self.context,
|
169
|
-
ordered: @options[:ordered],
|
170
174
|
framing: framing)
|
171
175
|
doc_base = @options[:base]
|
172
176
|
end
|
@@ -200,9 +204,9 @@ module JSON::LD
|
|
200
204
|
# The JSON-LD object to copy and perform the compaction upon.
|
201
205
|
# @param [String, #read, Hash, Array, JSON::LD::Context] context
|
202
206
|
# The base context to use when compacting the input.
|
207
|
+
# @param [Boolean] expanded (false) Input is already expanded
|
203
208
|
# @param [Hash{Symbol => Object}] options
|
204
209
|
# @option options (see #initialize)
|
205
|
-
# @option options [Boolean] :expanded Input is already expanded
|
206
210
|
# @yield jsonld
|
207
211
|
# @yieldparam [Hash] jsonld
|
208
212
|
# The compacted JSON-LD document
|
@@ -218,21 +222,21 @@ module JSON::LD
|
|
218
222
|
# 1) Perform the Expansion Algorithm on the JSON-LD input.
|
219
223
|
# This removes any existing context to allow the given context to be cleanly applied.
|
220
224
|
expanded_input = expanded ? input : API.expand(input, ordered: false, **options) do |res, base_iri|
|
221
|
-
options[:base] ||= base_iri if options[:compactToRelative]
|
225
|
+
options[:base] ||= RDF::URI(base_iri) if base_iri && options[:compactToRelative]
|
222
226
|
res
|
223
227
|
end
|
224
228
|
|
225
229
|
API.new(expanded_input, context, no_default_base: true, **options) do
|
226
230
|
log_debug(".compact") {"expanded input: #{expanded_input.to_json(JSON_STATE) rescue 'malformed json'}"}
|
227
|
-
result = compact(value
|
231
|
+
result = compact(value)
|
228
232
|
|
229
233
|
# xxx) Add the given context to the output
|
230
|
-
ctx = self.context.serialize
|
234
|
+
ctx = self.context.serialize(provided_context: context)
|
231
235
|
if result.is_a?(Array)
|
232
236
|
kwgraph = self.context.compact_iri('@graph', vocab: true)
|
233
237
|
result = result.empty? ? {} : {kwgraph => result}
|
234
238
|
end
|
235
|
-
result = ctx.merge(result) unless ctx.empty?
|
239
|
+
result = ctx.merge(result) unless ctx.fetch('@context', {}).empty?
|
236
240
|
end
|
237
241
|
block_given? ? yield(result) : result
|
238
242
|
end
|
@@ -246,9 +250,9 @@ module JSON::LD
|
|
246
250
|
# The JSON-LD object or array of JSON-LD objects to flatten or an IRI referencing the JSON-LD document to flatten.
|
247
251
|
# @param [String, #read, Hash, Array, JSON::LD::EvaluationContext] context
|
248
252
|
# An optional external context to use additionally to the context embedded in input when expanding the input.
|
253
|
+
# @param [Boolean] expanded (false) Input is already expanded
|
249
254
|
# @param [Hash{Symbol => Object}] options
|
250
255
|
# @option options (see #initialize)
|
251
|
-
# @option options [Boolean] :expanded Input is already expanded
|
252
256
|
# @yield jsonld
|
253
257
|
# @yieldparam [Hash] jsonld
|
254
258
|
# The flattened JSON-LD document
|
@@ -265,7 +269,7 @@ module JSON::LD
|
|
265
269
|
|
266
270
|
# Expand input to simplify processing
|
267
271
|
expanded_input = expanded ? input : API.expand(input, **options) do |result, base_iri|
|
268
|
-
options[:base] ||= base_iri if options[:compactToRelative]
|
272
|
+
options[:base] ||= RDF::URI(base_iri) if base_iri && options[:compactToRelative]
|
269
273
|
result
|
270
274
|
end
|
271
275
|
|
@@ -273,6 +277,9 @@ module JSON::LD
|
|
273
277
|
API.new(expanded_input, context, no_default_base: true, **options) do
|
274
278
|
log_debug(".flatten") {"expanded input: #{value.to_json(JSON_STATE) rescue 'malformed json'}"}
|
275
279
|
|
280
|
+
# Rename blank nodes recusively. Note that this does not create new blank node identifiers where none exist, which is performed in the node map generation algorithm.
|
281
|
+
@value = rename_bnodes(@value) if @options[:rename_bnodes]
|
282
|
+
|
276
283
|
# 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
284
|
graph_maps = {'@default' => {}}
|
278
285
|
create_node_map(value, graph_maps)
|
@@ -294,9 +301,11 @@ module JSON::LD
|
|
294
301
|
|
295
302
|
if context && !flattened.empty?
|
296
303
|
# 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.
|
297
|
-
compacted = as_array(compact(flattened
|
304
|
+
compacted = as_array(compact(flattened))
|
298
305
|
kwgraph = self.context.compact_iri('@graph')
|
299
|
-
flattened = self.context.
|
306
|
+
flattened = self.context.
|
307
|
+
serialize(provided_context: context).
|
308
|
+
merge(kwgraph => compacted)
|
300
309
|
end
|
301
310
|
end
|
302
311
|
|
@@ -312,6 +321,7 @@ module JSON::LD
|
|
312
321
|
# The JSON-LD object to copy and perform the framing on.
|
313
322
|
# @param [String, #read, Hash, Array] frame
|
314
323
|
# The frame to use when re-arranging the data.
|
324
|
+
# @param [Boolean] expanded (false) Input is already expanded
|
315
325
|
# @option options (see #initialize)
|
316
326
|
# @option options ['@always', '@link', '@once', '@never'] :embed ('@once')
|
317
327
|
# a flag specifying that objects should be directly embedded in the output, instead of being referred to by their IRI.
|
@@ -321,7 +331,6 @@ module JSON::LD
|
|
321
331
|
# A flag specifying that all properties present in the input frame must either have a default value or be present in the JSON-LD input for the frame to match.
|
322
332
|
# @option options [Boolean] :omitDefault (false)
|
323
333
|
# a flag specifying that properties that are missing from the JSON-LD input should be omitted from the output.
|
324
|
-
# @option options [Boolean] :expanded Input is already expanded
|
325
334
|
# @option options [Boolean] :pruneBlankNodeIdentifiers (true) removes blank node identifiers that are only used once.
|
326
335
|
# @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`.
|
327
336
|
# @yield jsonld
|
@@ -335,7 +344,7 @@ module JSON::LD
|
|
335
344
|
def self.frame(input, frame, expanded: false, **options)
|
336
345
|
result = nil
|
337
346
|
options = {
|
338
|
-
base: (input if input.is_a?(String)),
|
347
|
+
base: (RDF::URI(input) if input.is_a?(String)),
|
339
348
|
compactArrays: true,
|
340
349
|
compactToRelative: true,
|
341
350
|
embed: '@once',
|
@@ -369,7 +378,7 @@ module JSON::LD
|
|
369
378
|
|
370
379
|
# Expand input to simplify processing
|
371
380
|
expanded_input = expanded ? input : API.expand(input, ordered: false, **options) do |res, base_iri|
|
372
|
-
options[:base] ||= base_iri if options[:compactToRelative]
|
381
|
+
options[:base] ||= RDF::URI(base_iri) if base_iri && options[:compactToRelative]
|
373
382
|
res
|
374
383
|
end
|
375
384
|
|
@@ -387,10 +396,13 @@ module JSON::LD
|
|
387
396
|
end
|
388
397
|
|
389
398
|
# Set omitGraph option, if not present, based on processingMode
|
390
|
-
unless options.
|
399
|
+
unless options.key?(:omitGraph)
|
391
400
|
options[:omitGraph] = context.processingMode('json-ld-1.1')
|
392
401
|
end
|
393
402
|
|
403
|
+
# Rename blank nodes recusively. Note that this does not create new blank node identifiers where none exist, which is performed in the node map generation algorithm.
|
404
|
+
@value = rename_bnodes(@value)
|
405
|
+
|
394
406
|
# Get framing nodes from expanded input, replacing Blank Node identifiers as necessary
|
395
407
|
create_node_map(value, framing_state[:graphMap], active_graph: '@default')
|
396
408
|
|
@@ -411,7 +423,7 @@ module JSON::LD
|
|
411
423
|
frame(framing_state, framing_state[:subjects].keys.opt_sort(ordered: @options[:ordered]), (expanded_frame.first || {}), parent: result, **options)
|
412
424
|
|
413
425
|
# Default to based on processinMode
|
414
|
-
if !options.
|
426
|
+
if !options.key?(:pruneBlankNodeIdentifiers)
|
415
427
|
options[:pruneBlankNodeIdentifiers] = context.processingMode('json-ld-1.1')
|
416
428
|
end
|
417
429
|
|
@@ -426,7 +438,7 @@ module JSON::LD
|
|
426
438
|
log_debug(".frame") {"expanded result: #{result.to_json(JSON_STATE) rescue 'malformed json'}"}
|
427
439
|
|
428
440
|
# Compact result
|
429
|
-
compacted = compact(result
|
441
|
+
compacted = compact(result)
|
430
442
|
|
431
443
|
# @replace `@null` with nil, compacting arrays
|
432
444
|
compacted = cleanup_null(compacted)
|
@@ -434,11 +446,14 @@ module JSON::LD
|
|
434
446
|
|
435
447
|
# Add the given context to the output
|
436
448
|
result = if !compacted.is_a?(Array)
|
437
|
-
|
449
|
+
compacted
|
438
450
|
else
|
439
451
|
kwgraph = context.compact_iri('@graph')
|
440
|
-
|
452
|
+
{kwgraph => compacted}
|
441
453
|
end
|
454
|
+
# Only add context if one was provided
|
455
|
+
result = context.serialize(provided_context: frame).merge(result) if frame['@context']
|
456
|
+
|
442
457
|
log_debug(".frame") {"after compact: #{result.to_json(JSON_STATE) rescue 'malformed json'}"}
|
443
458
|
result
|
444
459
|
end
|
@@ -451,10 +466,10 @@ module JSON::LD
|
|
451
466
|
#
|
452
467
|
# @param [String, #read, Hash, Array] input
|
453
468
|
# The JSON-LD object to process when outputting statements.
|
469
|
+
# @param [Boolean] expanded (false) Input is already expanded
|
454
470
|
# @option options (see #initialize)
|
455
471
|
# @option options [Boolean] :produceGeneralizedRdf (false)
|
456
472
|
# If true, output will include statements having blank node predicates, otherwise they are dropped.
|
457
|
-
# @option options [Boolean] :expanded Input is already expanded
|
458
473
|
# @raise [JsonLdError]
|
459
474
|
# @yield statement
|
460
475
|
# @yieldparam [RDF::Statement] statement
|
@@ -463,7 +478,7 @@ module JSON::LD
|
|
463
478
|
unless block_given?
|
464
479
|
results = []
|
465
480
|
results.extend(RDF::Enumerable)
|
466
|
-
self.toRdf(input, **options) do |stmt|
|
481
|
+
self.toRdf(input, expanded: expanded, **options) do |stmt|
|
467
482
|
results << stmt
|
468
483
|
end
|
469
484
|
return results
|
@@ -473,16 +488,16 @@ module JSON::LD
|
|
473
488
|
extractAllScripts: true,
|
474
489
|
}.merge(options)
|
475
490
|
|
476
|
-
#
|
477
|
-
|
491
|
+
# Flatten input to simplify processing
|
492
|
+
flattened_input = API.flatten(input, nil, expanded: expanded, ordered: false, **options)
|
478
493
|
|
479
|
-
API.new(
|
494
|
+
API.new(flattened_input, nil, **options) do
|
480
495
|
# 1) Perform the Expansion Algorithm on the JSON-LD input.
|
481
496
|
# This removes any existing context to allow the given context to be cleanly applied.
|
482
|
-
log_debug(".toRdf") {"
|
497
|
+
log_debug(".toRdf") {"flattened input: #{flattened_input.to_json(JSON_STATE) rescue 'malformed json'}"}
|
483
498
|
|
484
499
|
# Recurse through input
|
485
|
-
|
500
|
+
flattened_input.each do |node|
|
486
501
|
item_to_rdf(node) do |statement|
|
487
502
|
next if statement.predicate.node? && !options[:produceGeneralizedRdf]
|
488
503
|
|
@@ -521,8 +536,7 @@ module JSON::LD
|
|
521
536
|
API.new(nil, nil, **options) do
|
522
537
|
result = from_statements(input,
|
523
538
|
useRdfType: useRdfType,
|
524
|
-
useNativeTypes: useNativeTypes
|
525
|
-
ordered: @options[:ordered])
|
539
|
+
useNativeTypes: useNativeTypes)
|
526
540
|
end
|
527
541
|
|
528
542
|
block_given? ? yield(result) : result
|
@@ -532,16 +546,18 @@ module JSON::LD
|
|
532
546
|
# Uses built-in or provided documentLoader to retrieve a parsed document.
|
533
547
|
#
|
534
548
|
# @param [RDF::URI, String] url
|
549
|
+
# @param [String, RDF::URI] base
|
550
|
+
# Location to use as documentUrl instead of `url`.
|
551
|
+
# @option options [Proc] :documentLoader
|
552
|
+
# The callback of the loader to be used to retrieve remote documents and contexts.
|
535
553
|
# @param [Boolean] extractAllScripts
|
536
554
|
# If set to `true`, when extracting JSON-LD script elements from HTML, unless a specific fragment identifier is targeted, extracts all encountered JSON-LD script elements using an array form, if necessary.
|
537
555
|
# @param [String] profile
|
538
556
|
# When the resulting `contentType` is `text/html` or `application/xhtml+xml`, this option determines the profile to use for selecting a JSON-LD script elements.
|
539
557
|
# @param [String] requestProfile
|
540
558
|
# One or more IRIs to use in the request as a profile parameter.
|
541
|
-
# @param [Boolean] validate
|
559
|
+
# @param [Boolean] validate (false)
|
542
560
|
# Allow only appropriate content types
|
543
|
-
# @param [String, RDF::URI] base
|
544
|
-
# Location to use as documentUrl instead of `url`.
|
545
561
|
# @param [Hash<Symbol => Object>] options
|
546
562
|
# @yield remote_document
|
547
563
|
# @yieldparam [RemoteDocumentRemoteDocument, RDF::Util::File::RemoteDocument] remote_document
|
@@ -550,13 +566,14 @@ module JSON::LD
|
|
550
566
|
# If a block is given, the result of evaluating the block is returned, otherwise, the retrieved remote document and context information unless block given
|
551
567
|
# @raise [JsonLdError]
|
552
568
|
def self.loadRemoteDocument(url,
|
569
|
+
base: nil,
|
570
|
+
documentLoader: nil,
|
553
571
|
extractAllScripts: false,
|
554
572
|
profile: nil,
|
555
573
|
requestProfile: nil,
|
556
574
|
validate: false,
|
557
|
-
base: nil,
|
558
575
|
**options)
|
559
|
-
documentLoader
|
576
|
+
documentLoader ||= self.method(:documentLoader)
|
560
577
|
options = OPEN_OPTS.merge(options)
|
561
578
|
if requestProfile
|
562
579
|
# Add any request profile
|
data/lib/json/ld/compact.rb
CHANGED
@@ -12,16 +12,16 @@ module JSON::LD
|
|
12
12
|
# This algorithm compacts a JSON-LD document, such that the given context is applied. This must result in shortening any applicable IRIs to terms or compact IRIs, any applicable keywords to keyword aliases, and any applicable JSON-LD values expressed in expanded form to simple values such as strings or numbers.
|
13
13
|
#
|
14
14
|
# @param [Array, Hash] element
|
15
|
-
# @param [String]
|
16
|
-
# @param [Boolean] ordered (true)
|
15
|
+
# @param [String, RDF::URI] base (nil)
|
17
16
|
# Ensure output objects have keys ordered properly
|
17
|
+
# @param [String] property (nil)
|
18
|
+
# Extra validatation
|
18
19
|
# @return [Array, Hash]
|
19
|
-
def compact(element,
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
#end
|
20
|
+
def compact(element,
|
21
|
+
base: nil,
|
22
|
+
property: nil,
|
23
|
+
log_depth: nil)
|
24
|
+
log_debug("compact", depth: log_depth.to_i) {"element: #{element.inspect}, ec: #{context.inspect}"}
|
25
25
|
|
26
26
|
# If the term definition for active property itself contains a context, use that for compacting values.
|
27
27
|
input_context = self.context
|
@@ -29,17 +29,19 @@ module JSON::LD
|
|
29
29
|
case element
|
30
30
|
when Array
|
31
31
|
#log_debug("") {"Array #{element.inspect}"}
|
32
|
-
result = element.map
|
32
|
+
result = element.map do |item|
|
33
|
+
compact(item, base: base, property: property, log_depth: log_depth.to_i + 1)
|
34
|
+
end.compact
|
33
35
|
|
34
36
|
# If element has a single member and the active property has no
|
35
37
|
# @container mapping to @list or @set, the compacted value is that
|
36
38
|
# member; otherwise the compacted value is element
|
37
39
|
if result.length == 1 &&
|
38
40
|
!context.as_array?(property) && @options[:compactArrays]
|
39
|
-
|
41
|
+
log_debug("=> extract single element", depth: log_depth.to_i) {result.first.inspect}
|
40
42
|
result.first
|
41
43
|
else
|
42
|
-
|
44
|
+
log_debug("=> array result", depth: log_depth.to_i) {result.inspect}
|
43
45
|
result
|
44
46
|
end
|
45
47
|
when Hash
|
@@ -50,24 +52,31 @@ module JSON::LD
|
|
50
52
|
|
51
53
|
# Revert any previously type-scoped (non-preserved) context
|
52
54
|
if context.previous_context && !element.key?('@value') && element.keys != %w(@id)
|
55
|
+
log_debug("revert ec", depth: log_depth.to_i) {"previous context: #{context.previous_context.inspect}"}
|
53
56
|
self.context = context.previous_context
|
54
57
|
end
|
55
58
|
|
56
59
|
# Look up term definintions from property using the original type-scoped context, if it exists, but apply them to the now current previous context
|
57
60
|
td = input_context.term_definitions[property] if property
|
58
|
-
|
61
|
+
if td && !td.context.nil?
|
62
|
+
self.context = context.parse(td.context,
|
63
|
+
override_protected: true)
|
64
|
+
log_debug("prop-scoped", depth: log_depth.to_i) {"context: #{self.context.inspect}"}
|
65
|
+
end
|
59
66
|
|
60
|
-
if element.key?('@id') || element.key?('@value')
|
61
|
-
result = context.compact_value(property, element,
|
67
|
+
if (element.key?('@id') || element.key?('@value')) && !element.key?('@annotation')
|
68
|
+
result = context.compact_value(property, element, base: @options[:base])
|
62
69
|
if !result.is_a?(Hash) || context.coerce(property) == '@json'
|
63
|
-
|
70
|
+
log_debug("", depth: log_depth.to_i) {"=> scalar result: #{result.inspect}"}
|
64
71
|
return result
|
65
72
|
end
|
66
73
|
end
|
67
74
|
|
68
75
|
# If expanded property is @list and we're contained within a list container, recursively compact this item to an array
|
69
76
|
if list?(element) && context.container(property).include?('@list')
|
70
|
-
return compact(element['@list'],
|
77
|
+
return compact(element['@list'], base: base,
|
78
|
+
property: property,
|
79
|
+
log_depth: log_depth.to_i + 1)
|
71
80
|
end
|
72
81
|
|
73
82
|
inside_reverse = property == '@reverse'
|
@@ -80,15 +89,25 @@ module JSON::LD
|
|
80
89
|
sort.
|
81
90
|
each do |term|
|
82
91
|
term_context = input_context.term_definitions[term].context if input_context.term_definitions[term]
|
83
|
-
self.context = context.parse(term_context, propagate: false)
|
92
|
+
self.context = context.parse(term_context, propagate: false) unless term_context.nil?
|
93
|
+
log_debug("type-scoped", depth: log_depth.to_i) {"context: #{self.context.inspect}"}
|
84
94
|
end
|
85
95
|
|
86
|
-
element.keys.opt_sort(ordered: ordered).each do |expanded_property|
|
96
|
+
element.keys.opt_sort(ordered: @options[:ordered]).each do |expanded_property|
|
87
97
|
expanded_value = element[expanded_property]
|
88
|
-
|
98
|
+
log_debug("", depth: log_depth.to_i) {"#{expanded_property}: #{expanded_value.inspect}"}
|
89
99
|
|
90
100
|
if expanded_property == '@id'
|
91
|
-
compacted_value =
|
101
|
+
compacted_value = as_array(expanded_value).map do |expanded_id|
|
102
|
+
if node?(expanded_id) && @options[:rdfstar]
|
103
|
+
# This can only really happen for valid RDF*
|
104
|
+
compact(expanded_id, base: base,
|
105
|
+
property: '@id',
|
106
|
+
log_depth: log_depth.to_i + 1)
|
107
|
+
else
|
108
|
+
context.compact_iri(expanded_id, base: @options[:base])
|
109
|
+
end
|
110
|
+
end
|
92
111
|
|
93
112
|
kw_alias = context.compact_iri('@id', vocab: true)
|
94
113
|
as_array = compacted_value.length > 1
|
@@ -98,7 +117,9 @@ module JSON::LD
|
|
98
117
|
end
|
99
118
|
|
100
119
|
if expanded_property == '@type'
|
101
|
-
compacted_value = Array(expanded_value).map
|
120
|
+
compacted_value = Array(expanded_value).map do |expanded_type|
|
121
|
+
input_context.compact_iri(expanded_type, vocab: true)
|
122
|
+
end
|
102
123
|
|
103
124
|
kw_alias = context.compact_iri('@type', vocab: true)
|
104
125
|
as_array = compacted_value.length > 1 ||
|
@@ -110,8 +131,10 @@ module JSON::LD
|
|
110
131
|
end
|
111
132
|
|
112
133
|
if expanded_property == '@reverse'
|
113
|
-
compacted_value = compact(expanded_value,
|
114
|
-
|
134
|
+
compacted_value = compact(expanded_value, base: base,
|
135
|
+
property: '@reverse',
|
136
|
+
log_depth: log_depth.to_i + 1)
|
137
|
+
log_debug("@reverse", depth: log_depth.to_i) {"compacted_value: #{compacted_value.inspect}"}
|
115
138
|
# handle double-reversed properties
|
116
139
|
compacted_value.each do |prop, value|
|
117
140
|
if context.reverse?(prop)
|
@@ -123,7 +146,7 @@ module JSON::LD
|
|
123
146
|
|
124
147
|
unless compacted_value.empty?
|
125
148
|
al = context.compact_iri('@reverse')
|
126
|
-
|
149
|
+
log_debug("", depth: log_depth.to_i) {"remainder: #{al} => #{compacted_value.inspect}"}
|
127
150
|
result[al] = compacted_value
|
128
151
|
end
|
129
152
|
next
|
@@ -131,8 +154,10 @@ module JSON::LD
|
|
131
154
|
|
132
155
|
if expanded_property == '@preserve'
|
133
156
|
# Compact using `property`
|
134
|
-
compacted_value = compact(expanded_value,
|
135
|
-
|
157
|
+
compacted_value = compact(expanded_value, base: base,
|
158
|
+
property: property,
|
159
|
+
log_depth: log_depth.to_i + 1)
|
160
|
+
log_debug("@preserve", depth: log_depth.to_i) {"compacted_value: #{compacted_value.inspect}"}
|
136
161
|
|
137
162
|
unless compacted_value.is_a?(Array) && compacted_value.empty?
|
138
163
|
result['@preserve'] = compacted_value
|
@@ -141,14 +166,14 @@ module JSON::LD
|
|
141
166
|
end
|
142
167
|
|
143
168
|
if expanded_property == '@index' && context.container(property).include?('@index')
|
144
|
-
|
169
|
+
log_debug("@index", depth: log_depth.to_i) {"drop @index"}
|
145
170
|
next
|
146
171
|
end
|
147
172
|
|
148
173
|
# Otherwise, if expanded property is @direction, @index, @value, or @language:
|
149
174
|
if EXPANDED_PROPERTY_DIRECTION_INDEX_LANGUAGE_VALUE.include?(expanded_property)
|
150
175
|
al = context.compact_iri(expanded_property, vocab: true)
|
151
|
-
|
176
|
+
log_debug(expanded_property, depth: log_depth.to_i) {"#{al} => #{expanded_value.inspect}"}
|
152
177
|
result[al] = expanded_value
|
153
178
|
next
|
154
179
|
end
|
@@ -158,8 +183,7 @@ module JSON::LD
|
|
158
183
|
context.compact_iri(expanded_property,
|
159
184
|
value: expanded_value,
|
160
185
|
vocab: true,
|
161
|
-
reverse: inside_reverse
|
162
|
-
log_depth: @options[:log_depth])
|
186
|
+
reverse: inside_reverse)
|
163
187
|
|
164
188
|
if nest_prop = context.nest(item_active_property)
|
165
189
|
result[nest_prop] ||= {}
|
@@ -177,8 +201,7 @@ module JSON::LD
|
|
177
201
|
context.compact_iri(expanded_property,
|
178
202
|
value: expanded_item,
|
179
203
|
vocab: true,
|
180
|
-
reverse: inside_reverse
|
181
|
-
log_depth: @options[:log_depth])
|
204
|
+
reverse: inside_reverse)
|
182
205
|
|
183
206
|
|
184
207
|
nest_result = if nest_prop = context.nest(item_active_property)
|
@@ -197,8 +220,10 @@ module JSON::LD
|
|
197
220
|
else expanded_item
|
198
221
|
end
|
199
222
|
|
200
|
-
compacted_item = compact(value,
|
201
|
-
|
223
|
+
compacted_item = compact(value, base: base,
|
224
|
+
property: item_active_property,
|
225
|
+
log_depth: log_depth.to_i + 1)
|
226
|
+
log_debug("", depth: log_depth.to_i) {" => compacted key: #{item_active_property.inspect} for #{compacted_item.inspect}"}
|
202
227
|
|
203
228
|
# handle @list
|
204
229
|
if list?(expanded_item)
|
@@ -206,7 +231,7 @@ module JSON::LD
|
|
206
231
|
unless container.include?('@list')
|
207
232
|
al = context.compact_iri('@list', vocab: true)
|
208
233
|
compacted_item = {al => compacted_item}
|
209
|
-
if expanded_item.
|
234
|
+
if expanded_item.key?('@index')
|
210
235
|
key = context.compact_iri('@index', vocab: true)
|
211
236
|
compacted_item[key] = expanded_item['@index']
|
212
237
|
end
|
@@ -225,9 +250,9 @@ module JSON::LD
|
|
225
250
|
map_object = nest_result[item_active_property] ||= {}
|
226
251
|
# If there is no @id, create a blank node identifier to use as an index
|
227
252
|
map_key = if container.include?('@id') && expanded_item['@id']
|
228
|
-
context.compact_iri(expanded_item['@id'])
|
253
|
+
context.compact_iri(expanded_item['@id'], base: @options[:base])
|
229
254
|
elsif container.include?('@index') && expanded_item['@index']
|
230
|
-
context.compact_iri(expanded_item['@index'])
|
255
|
+
context.compact_iri(expanded_item['@index'], vocab: true)
|
231
256
|
else
|
232
257
|
context.compact_iri('@none', vocab: true)
|
233
258
|
end
|
@@ -251,7 +276,7 @@ module JSON::LD
|
|
251
276
|
al = context.compact_iri('@id', vocab: true)
|
252
277
|
compacted_item[al] = context.compact_iri(expanded_item['@id'], vocab: false)
|
253
278
|
end
|
254
|
-
if expanded_item.
|
279
|
+
if expanded_item.key?('@index')
|
255
280
|
key = context.compact_iri('@index', vocab: true)
|
256
281
|
compacted_item[key] = expanded_item['@index']
|
257
282
|
end
|
@@ -299,7 +324,10 @@ module JSON::LD
|
|
299
324
|
|
300
325
|
# if compacted_item contains a single entry who's key maps to @id, then recompact the item without @type
|
301
326
|
if compacted_item.keys.length == 1 && expanded_item.keys.include?('@id')
|
302
|
-
compacted_item = compact({'@id' => expanded_item['@id']},
|
327
|
+
compacted_item = compact({'@id' => expanded_item['@id']},
|
328
|
+
base: base,
|
329
|
+
property: item_active_property,
|
330
|
+
log_depth: log_depth.to_i + 1)
|
303
331
|
end
|
304
332
|
compacted_item
|
305
333
|
end
|
@@ -316,7 +344,7 @@ module JSON::LD
|
|
316
344
|
result
|
317
345
|
else
|
318
346
|
# For other types, the compacted value is the element value
|
319
|
-
|
347
|
+
log_debug("compact", depth: log_depth.to_i) {element.class.to_s}
|
320
348
|
element
|
321
349
|
end
|
322
350
|
|