json-ld 3.1.3 → 3.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/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
|
|