json-ld 3.1.0 → 3.1.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +66 -46
- data/VERSION +1 -1
- data/bin/jsonld +27 -30
- data/lib/json/ld.rb +12 -8
- data/lib/json/ld/api.rb +51 -43
- data/lib/json/ld/compact.rb +82 -68
- data/lib/json/ld/conneg.rb +1 -1
- data/lib/json/ld/context.rb +650 -542
- data/lib/json/ld/expand.rb +154 -87
- data/lib/json/ld/flatten.rb +1 -1
- data/lib/json/ld/format.rb +10 -6
- data/lib/json/ld/frame.rb +1 -2
- data/lib/json/ld/from_rdf.rb +7 -8
- data/lib/json/ld/html/nokogiri.rb +2 -1
- data/lib/json/ld/html/rexml.rb +2 -1
- data/lib/json/ld/reader.rb +20 -11
- data/lib/json/ld/streaming_reader.rb +578 -0
- data/lib/json/ld/to_rdf.rb +9 -3
- data/lib/json/ld/writer.rb +12 -5
- data/spec/compact_spec.rb +1 -0
- data/spec/context_spec.rb +63 -116
- data/spec/expand_spec.rb +29 -9
- data/spec/frame_spec.rb +44 -0
- data/spec/matchers.rb +1 -1
- data/spec/reader_spec.rb +33 -34
- data/spec/streaming_reader_spec.rb +237 -0
- data/spec/suite_expand_spec.rb +4 -2
- data/spec/suite_frame_spec.rb +0 -1
- data/spec/suite_helper.rb +23 -8
- data/spec/suite_to_rdf_spec.rb +1 -1
- data/spec/to_rdf_spec.rb +3 -3
- metadata +11 -8
data/lib/json/ld/expand.rb
CHANGED
@@ -9,13 +9,8 @@ module JSON::LD
|
|
9
9
|
include Utils
|
10
10
|
|
11
11
|
# The following constant is used to reduce object allocations
|
12
|
-
CONTAINER_INDEX_ID_TYPE = Set
|
13
|
-
|
14
|
-
CONTAINER_INDEX = %w(@index).freeze
|
15
|
-
CONTAINER_ID = %w(@id).freeze
|
16
|
-
CONTAINER_LIST = %w(@list).freeze
|
17
|
-
CONTAINER_TYPE = %w(@type).freeze
|
18
|
-
CONTAINER_GRAPH_ID = %w(@graph @id).freeze
|
12
|
+
CONTAINER_INDEX_ID_TYPE = Set['@index', '@id', '@type'].freeze
|
13
|
+
KEY_ID = %w(@id).freeze
|
19
14
|
KEYS_VALUE_LANGUAGE_TYPE_INDEX_DIRECTION = %w(@value @language @type @index @direction).freeze
|
20
15
|
KEYS_SET_LIST_INDEX = %w(@set @list @index).freeze
|
21
16
|
KEYS_INCLUDED_TYPE = %w(@included @type).freeze
|
@@ -26,28 +21,32 @@ module JSON::LD
|
|
26
21
|
# @param [Array, Hash] input
|
27
22
|
# @param [String] active_property
|
28
23
|
# @param [Context] context
|
29
|
-
# @param [Boolean] ordered (true)
|
30
|
-
# Ensure output objects have keys ordered properly
|
31
24
|
# @param [Boolean] framing (false)
|
32
25
|
# Special rules for expanding a frame
|
33
26
|
# @param [Boolean] from_map
|
34
27
|
# Expanding from a map, which could be an `@type` map, so don't clear out context term definitions
|
28
|
+
#
|
35
29
|
# @return [Array<Hash{String => Object}>]
|
36
|
-
def expand(input, active_property, context,
|
37
|
-
|
30
|
+
def expand(input, active_property, context,
|
31
|
+
framing: false, from_map: false, log_depth: nil)
|
32
|
+
log_debug("expand", depth: log_depth.to_i) {"input: #{input.inspect}, active_property: #{active_property.inspect}, context: #{context.inspect}"}
|
38
33
|
framing = false if active_property == '@default'
|
39
|
-
expanded_active_property = context.expand_iri(active_property, vocab: true)
|
34
|
+
expanded_active_property = context.expand_iri(active_property, vocab: true, as_string: true, base: @options[:base]) if active_property
|
40
35
|
|
41
36
|
# Use a term-specific context, if defined, based on the non-type-scoped context.
|
42
37
|
property_scoped_context = context.term_definitions[active_property].context if active_property && context.term_definitions[active_property]
|
38
|
+
log_debug("expand", depth: log_depth.to_i) {"property_scoped_context: #{property_scoped_context.inspect}"} unless property_scoped_context.nil?
|
43
39
|
|
44
40
|
result = case input
|
45
41
|
when Array
|
46
42
|
# If element is an array,
|
47
|
-
is_list = context.container(active_property)
|
43
|
+
is_list = context.container(active_property).include?('@list')
|
48
44
|
value = input.each_with_object([]) do |v, memo|
|
49
45
|
# Initialize expanded item to the result of using this algorithm recursively, passing active context, active property, and item as element.
|
50
|
-
v = expand(v, active_property, context,
|
46
|
+
v = expand(v, active_property, context,
|
47
|
+
framing: framing,
|
48
|
+
from_map: from_map,
|
49
|
+
log_depth: log_depth.to_i + 1)
|
51
50
|
|
52
51
|
# If the active property is @list or its container mapping is set to @list and v is an array, change it to a list object
|
53
52
|
v = {"@list" => v} if is_list && v.is_a?(Array)
|
@@ -62,23 +61,29 @@ module JSON::LD
|
|
62
61
|
value
|
63
62
|
when Hash
|
64
63
|
if context.previous_context
|
65
|
-
expanded_key_map = input.keys.inject({})
|
64
|
+
expanded_key_map = input.keys.inject({}) do |memo, key|
|
65
|
+
memo.merge(key => context.expand_iri(key, vocab: true, as_string: true, base: @options[:base]))
|
66
|
+
end
|
66
67
|
# Revert any previously type-scoped term definitions, unless this is from a map, a value object or a subject reference
|
67
68
|
revert_context = !from_map &&
|
68
69
|
!expanded_key_map.values.include?('@value') &&
|
69
70
|
!(expanded_key_map.values == ['@id'])
|
70
71
|
|
71
72
|
# If there's a previous context, the context was type-scoped
|
73
|
+
log_debug("expand", depth: log_depth.to_i) {"previous_context: #{context.previous_context.inspect}"} if revert_context
|
72
74
|
context = context.previous_context if revert_context
|
73
75
|
end
|
74
76
|
|
75
77
|
# Apply property-scoped context after reverting term-scoped context
|
76
|
-
|
78
|
+
unless property_scoped_context.nil?
|
79
|
+
context = context.parse(property_scoped_context, base: @options[:base], override_protected: true)
|
80
|
+
end
|
81
|
+
log_debug("expand", depth: log_depth.to_i) {"after property_scoped_context: #{context.inspect}"} unless property_scoped_context.nil?
|
77
82
|
|
78
83
|
# If element contains the key @context, set active context to the result of the Context Processing algorithm, passing active context and the value of the @context key as local context.
|
79
84
|
if input.has_key?('@context')
|
80
|
-
context = context.parse(input.delete('@context'))
|
81
|
-
|
85
|
+
context = context.parse(input.delete('@context'), base: @options[:base])
|
86
|
+
log_debug("expand", depth: log_depth.to_i) {"context: #{context.inspect}"}
|
82
87
|
end
|
83
88
|
|
84
89
|
# Set the type-scoped context to the context on input, for use later
|
@@ -89,25 +94,28 @@ module JSON::LD
|
|
89
94
|
# See if keys mapping to @type have terms with a local context
|
90
95
|
type_key = nil
|
91
96
|
input.keys.sort.
|
92
|
-
select {|k| context.expand_iri(k, vocab: true,
|
97
|
+
select {|k| context.expand_iri(k, vocab: true, base: @options[:base]) == '@type'}.
|
93
98
|
each do |tk|
|
94
99
|
|
95
100
|
type_key ||= tk # Side effect saves the first found key mapping to @type
|
96
101
|
Array(input[tk]).sort.each do |term|
|
97
102
|
term_context = type_scoped_context.term_definitions[term].context if type_scoped_context.term_definitions[term]
|
98
|
-
|
103
|
+
unless term_context.nil?
|
104
|
+
log_debug("expand", depth: log_depth.to_i) {"term_context: #{term_context.inspect}"}
|
105
|
+
context = context.parse(term_context, base: @options[:base], propagate: false)
|
106
|
+
end
|
99
107
|
end
|
100
108
|
end
|
101
109
|
|
102
110
|
# Process each key and value in element. Ignores @nesting content
|
103
111
|
expand_object(input, active_property, context, output_object,
|
104
112
|
expanded_active_property: expanded_active_property,
|
105
|
-
|
113
|
+
framing: framing,
|
106
114
|
type_key: type_key,
|
107
|
-
|
108
|
-
|
115
|
+
type_scoped_context: type_scoped_context,
|
116
|
+
log_depth: log_depth.to_i + 1)
|
109
117
|
|
110
|
-
|
118
|
+
log_debug("output object", depth: log_depth.to_i) {output_object.inspect}
|
111
119
|
|
112
120
|
# If result contains the key @value:
|
113
121
|
if value?(output_object)
|
@@ -115,13 +123,13 @@ module JSON::LD
|
|
115
123
|
unless (keys - KEYS_VALUE_LANGUAGE_TYPE_INDEX_DIRECTION).empty?
|
116
124
|
# The result must not contain any keys other than @direction, @value, @language, @type, and @index. It must not contain both the @language key and the @type key. Otherwise, an invalid value object error has been detected and processing is aborted.
|
117
125
|
raise JsonLdError::InvalidValueObject,
|
118
|
-
|
126
|
+
"value object has unknown keys: #{output_object.inspect}"
|
119
127
|
end
|
120
128
|
|
121
129
|
if keys.include?('@type') && !(keys & %w(@language @direction)).empty?
|
122
130
|
# @type is inconsistent with either @language or @direction
|
123
131
|
raise JsonLdError::InvalidValueObject,
|
124
|
-
|
132
|
+
"value object must not include @type with either @language or @direction: #{output_object.inspect}"
|
125
133
|
end
|
126
134
|
|
127
135
|
output_object.delete('@language') if output_object.key?('@language') && Array(output_object['@language']).empty?
|
@@ -138,9 +146,16 @@ module JSON::LD
|
|
138
146
|
# Otherwise, if the value of result's @value member is not a string and result contains the key @language, an invalid language-tagged value error has been detected (only strings can be language-tagged) and processing is aborted.
|
139
147
|
raise JsonLdError::InvalidLanguageTaggedValue,
|
140
148
|
"when @language is used, @value must be a string: #{output_object.inspect}"
|
141
|
-
elsif
|
142
|
-
|
143
|
-
|
149
|
+
elsif output_object['@type'] &&
|
150
|
+
(!Array(output_object['@type']).all? {|t|
|
151
|
+
t.is_a?(String) && RDF::URI(t).valid? && !t.start_with?('_:') ||
|
152
|
+
t.is_a?(Hash) && t.empty?} ||
|
153
|
+
!framing && !output_object['@type'].is_a?(String))
|
154
|
+
# Otherwise, if the result has a @type member and its value is not an IRI, an invalid typed value error has been detected and processing is aborted.
|
155
|
+
raise JsonLdError::InvalidTypedValue,
|
156
|
+
"value of @type must be an IRI or '@json': #{output_object.inspect}"
|
157
|
+
elsif !framing && !output_object.fetch('@type', '').is_a?(String) &&
|
158
|
+
RDF::URI(t).valid? && !t.start_with?('_:')
|
144
159
|
# Otherwise, if the result has a @type member and its value is not an IRI, an invalid typed value error has been detected and processing is aborted.
|
145
160
|
raise JsonLdError::InvalidTypedValue,
|
146
161
|
"value of @type must be an IRI or '@json': #{output_object.inspect}"
|
@@ -164,14 +179,14 @@ module JSON::LD
|
|
164
179
|
|
165
180
|
# If active property is null or @graph, drop free-floating values as follows:
|
166
181
|
if (expanded_active_property || '@graph') == '@graph' &&
|
167
|
-
|
168
|
-
(output_object.keys -
|
169
|
-
|
182
|
+
(output_object.key?('@value') || output_object.key?('@list') ||
|
183
|
+
(output_object.keys - KEY_ID).empty? && !framing)
|
184
|
+
log_debug(" =>", depth: log_depth.to_i) { "empty top-level: " + output_object.inspect}
|
170
185
|
return nil
|
171
186
|
end
|
172
187
|
|
173
188
|
# Re-order result keys if ordering
|
174
|
-
if ordered
|
189
|
+
if @options[:ordered]
|
175
190
|
output_object.keys.sort.each_with_object({}) {|kk, memo| memo[kk] = output_object[kk]}
|
176
191
|
else
|
177
192
|
output_object
|
@@ -181,12 +196,17 @@ module JSON::LD
|
|
181
196
|
return nil if input.nil? || active_property.nil? || expanded_active_property == '@graph'
|
182
197
|
|
183
198
|
# Apply property-scoped context
|
184
|
-
|
199
|
+
unless property_scoped_context.nil?
|
200
|
+
context = context.parse(property_scoped_context,
|
201
|
+
base: @options[:base],
|
202
|
+
override_protected: true)
|
203
|
+
end
|
204
|
+
log_debug("expand", depth: log_depth.to_i) {"property_scoped_context: #{context.inspect}"} unless property_scoped_context.nil?
|
185
205
|
|
186
|
-
context.expand_value(active_property, input,
|
206
|
+
context.expand_value(active_property, input, base: @options[:base])
|
187
207
|
end
|
188
208
|
|
189
|
-
|
209
|
+
log_debug(depth: log_depth.to_i) {" => #{result.inspect}"}
|
190
210
|
result
|
191
211
|
end
|
192
212
|
|
@@ -195,21 +215,21 @@ module JSON::LD
|
|
195
215
|
# Expand each key and value of element adding them to result
|
196
216
|
def expand_object(input, active_property, context, output_object,
|
197
217
|
expanded_active_property:,
|
198
|
-
|
218
|
+
framing:,
|
199
219
|
type_key:,
|
200
|
-
|
201
|
-
|
220
|
+
type_scoped_context:,
|
221
|
+
log_depth: nil)
|
202
222
|
nests = []
|
203
223
|
|
204
224
|
input_type = Array(input[type_key]).last
|
205
|
-
input_type = context.expand_iri(input_type, vocab: true,
|
225
|
+
input_type = context.expand_iri(input_type, vocab: true, as_string: true, base: @options[:base]) if input_type
|
206
226
|
|
207
227
|
# Then, proceed and process each property and value in element as follows:
|
208
|
-
keys = ordered ? input.keys.sort : input.keys
|
228
|
+
keys = @options[:ordered] ? input.keys.sort : input.keys
|
209
229
|
keys.each do |key|
|
210
230
|
# For each key and value in element, ordered lexicographically by key:
|
211
231
|
value = input[key]
|
212
|
-
expanded_property = context.expand_iri(key, vocab: true,
|
232
|
+
expanded_property = context.expand_iri(key, vocab: true, base: @options[:base])
|
213
233
|
|
214
234
|
# If expanded property is null or it neither contains a colon (:) nor it is a keyword, drop key by continuing to the next key.
|
215
235
|
next if expanded_property.is_a?(RDF::URI) && expanded_property.relative?
|
@@ -220,10 +240,10 @@ module JSON::LD
|
|
220
240
|
expanded_property.to_s.start_with?("_:") &&
|
221
241
|
context.processingMode('json-ld-1.1')
|
222
242
|
|
223
|
-
#log_debug("expand property") {"ap: #{active_property.inspect}, expanded: #{expanded_property.inspect}, value: #{value.inspect}"}
|
243
|
+
#log_debug("expand property", depth: log_depth.to_i) {"ap: #{active_property.inspect}, expanded: #{expanded_property.inspect}, value: #{value.inspect}"}
|
224
244
|
|
225
245
|
if expanded_property.nil?
|
226
|
-
#log_debug(" => ") {"skip nil property"}
|
246
|
+
#log_debug(" => ", depth: log_depth.to_i) {"skip nil property"}
|
227
247
|
next
|
228
248
|
end
|
229
249
|
|
@@ -241,16 +261,16 @@ module JSON::LD
|
|
241
261
|
# If expanded property is @id and value is not a string, an invalid @id value error has been detected and processing is aborted
|
242
262
|
e_id = case value
|
243
263
|
when String
|
244
|
-
context.expand_iri(value,
|
264
|
+
context.expand_iri(value, as_string: true, base: @options[:base], documentRelative: true)
|
245
265
|
when Array
|
246
266
|
# Framing allows an array of IRIs, and always puts values in an array
|
247
267
|
raise JsonLdError::InvalidIdValue,
|
248
268
|
"value of @id must be a string unless framing: #{value.inspect}" unless framing
|
249
|
-
context.expand_iri(value,
|
269
|
+
context.expand_iri(value, as_string: true, base: @options[:base], documentRelative: true)
|
250
270
|
value.map do |v|
|
251
271
|
raise JsonLdError::InvalidTypeValue,
|
252
272
|
"@id value must be a string or array of strings for framing: #{v.inspect}" unless v.is_a?(String)
|
253
|
-
context.expand_iri(v,
|
273
|
+
context.expand_iri(v, as_string: true, base: @options[:base], documentRelative: true)
|
254
274
|
end
|
255
275
|
when Hash
|
256
276
|
raise JsonLdError::InvalidIdValue,
|
@@ -273,7 +293,9 @@ module JSON::LD
|
|
273
293
|
when '@included'
|
274
294
|
# Included blocks are treated as an array of separate object nodes sharing the same referencing active_property. For 1.0, it is skipped as are other unknown keywords
|
275
295
|
next if context.processingMode('json-ld-1.0')
|
276
|
-
included_result = as_array(expand(value, active_property, context,
|
296
|
+
included_result = as_array(expand(value, active_property, context,
|
297
|
+
framing: framing,
|
298
|
+
log_depth: log_depth.to_i + 1))
|
277
299
|
|
278
300
|
# Expanded values must be node objects
|
279
301
|
raise JsonLdError::InvalidIncludedValue, "values of @included must expand to node objects" unless included_result.all? {|e| node?(e)}
|
@@ -281,27 +303,39 @@ module JSON::LD
|
|
281
303
|
Array(output_object['@included']) + included_result
|
282
304
|
when '@type'
|
283
305
|
# If expanded property is @type and value is neither a string nor an array of strings, an invalid type value error has been detected and processing is aborted. Otherwise, set expanded value to the result of using the IRI Expansion algorithm, passing active context, true for vocab, and true for document relative to expand the value or each of its items.
|
284
|
-
#log_debug("@type") {"value: #{value.inspect}"}
|
306
|
+
#log_debug("@type", depth: log_depth.to_i) {"value: #{value.inspect}"}
|
285
307
|
e_type = case value
|
286
308
|
when Array
|
287
309
|
value.map do |v|
|
288
310
|
raise JsonLdError::InvalidTypeValue,
|
289
311
|
"@type value must be a string or array of strings: #{v.inspect}" unless v.is_a?(String)
|
290
|
-
type_scoped_context.expand_iri(v,
|
312
|
+
type_scoped_context.expand_iri(v,
|
313
|
+
as_string: true,
|
314
|
+
base: @options[:base],
|
315
|
+
documentRelative: true,
|
316
|
+
vocab: true)
|
291
317
|
end
|
292
318
|
when String
|
293
|
-
type_scoped_context.expand_iri(value,
|
319
|
+
type_scoped_context.expand_iri(value,
|
320
|
+
as_string: true,
|
321
|
+
base: @options[:base],
|
322
|
+
documentRelative: true,
|
323
|
+
vocab: true)
|
294
324
|
when Hash
|
295
325
|
if !framing
|
296
326
|
raise JsonLdError::InvalidTypeValue,
|
297
327
|
"@type value must be a string or array of strings: #{value.inspect}"
|
298
328
|
elsif value.keys.length == 1 &&
|
299
|
-
type_scoped_context.expand_iri(value.keys.first, vocab: true,
|
329
|
+
type_scoped_context.expand_iri(value.keys.first, vocab: true, base: @options[:base]) == '@default'
|
300
330
|
# Expand values of @default, which must be a string, or array of strings expanding to IRIs
|
301
331
|
[{'@default' => Array(value['@default']).map do |v|
|
302
332
|
raise JsonLdError::InvalidTypeValue,
|
303
333
|
"@type default value must be a string or array of strings: #{v.inspect}" unless v.is_a?(String)
|
304
|
-
type_scoped_context.expand_iri(v,
|
334
|
+
type_scoped_context.expand_iri(v,
|
335
|
+
as_string: true,
|
336
|
+
base: @options[:base],
|
337
|
+
documentRelative: true,
|
338
|
+
vocab: true)
|
305
339
|
end}]
|
306
340
|
elsif !value.empty?
|
307
341
|
raise JsonLdError::InvalidTypeValue,
|
@@ -319,7 +353,9 @@ module JSON::LD
|
|
319
353
|
framing || e_type.length > 1 ? e_type : e_type.first
|
320
354
|
when '@graph'
|
321
355
|
# If expanded property is @graph, set expanded value to the result of using this algorithm recursively passing active context, @graph for active property, and value for element.
|
322
|
-
value = expand(value, '@graph', context,
|
356
|
+
value = expand(value, '@graph', context,
|
357
|
+
framing: framing,
|
358
|
+
log_depth: log_depth.to_i + 1)
|
323
359
|
as_array(value)
|
324
360
|
when '@value'
|
325
361
|
# If expanded property is @value and input contains @type: json, accept any value.
|
@@ -411,7 +447,9 @@ module JSON::LD
|
|
411
447
|
next if (expanded_active_property || '@graph') == '@graph'
|
412
448
|
|
413
449
|
# Otherwise, initialize expanded value to the result of using this algorithm recursively passing active context, active property, and value for element.
|
414
|
-
value = expand(value, active_property, context,
|
450
|
+
value = expand(value, active_property, context,
|
451
|
+
framing: framing,
|
452
|
+
log_depth: log_depth.to_i + 1)
|
415
453
|
|
416
454
|
# Spec FIXME: need to be sure that result is an array
|
417
455
|
value = as_array(value)
|
@@ -419,7 +457,9 @@ module JSON::LD
|
|
419
457
|
value
|
420
458
|
when '@set'
|
421
459
|
# If expanded property is @set, set expanded value to the result of using this algorithm recursively, passing active context, active property, and value for element.
|
422
|
-
expand(value, active_property, context,
|
460
|
+
expand(value, active_property, context,
|
461
|
+
framing: framing,
|
462
|
+
log_depth: log_depth.to_i + 1)
|
423
463
|
when '@reverse'
|
424
464
|
# If expanded property is @reverse and value is not a JSON object, an invalid @reverse value error has been detected and processing is aborted.
|
425
465
|
raise JsonLdError::InvalidReverseValue,
|
@@ -427,11 +467,13 @@ module JSON::LD
|
|
427
467
|
|
428
468
|
# Otherwise
|
429
469
|
# Initialize expanded value to the result of using this algorithm recursively, passing active context, @reverse as active property, and value as element.
|
430
|
-
value = expand(value, '@reverse', context,
|
470
|
+
value = expand(value, '@reverse', context,
|
471
|
+
framing: framing,
|
472
|
+
log_depth: log_depth.to_i + 1)
|
431
473
|
|
432
474
|
# If expanded value contains an @reverse member, i.e., properties that are reversed twice, execute for each of its property and item the following steps:
|
433
475
|
if value.has_key?('@reverse')
|
434
|
-
#log_debug("@reverse") {"double reverse: #{value.inspect}"}
|
476
|
+
#log_debug("@reverse", depth: log_depth.to_i) {"double reverse: #{value.inspect}"}
|
435
477
|
value['@reverse'].each do |property, item|
|
436
478
|
# If result does not have a property member, create one and set its value to an empty array.
|
437
479
|
# Append item to the value of the property member of result.
|
@@ -460,7 +502,10 @@ module JSON::LD
|
|
460
502
|
when '@default', '@embed', '@explicit', '@omitDefault', '@preserve', '@requireAll'
|
461
503
|
next unless framing
|
462
504
|
# Framing keywords
|
463
|
-
[expand(value, expanded_property, context,
|
505
|
+
[expand(value, expanded_property, context,
|
506
|
+
framing: framing,
|
507
|
+
log_depth: log_depth.to_i + 1)
|
508
|
+
].flatten
|
464
509
|
when '@nest'
|
465
510
|
# Add key to nests
|
466
511
|
nests << key
|
@@ -472,7 +517,7 @@ module JSON::LD
|
|
472
517
|
end
|
473
518
|
|
474
519
|
# Unless expanded value is null, set the expanded property member of result to expanded value.
|
475
|
-
#log_debug("expand #{expanded_property}") { expanded_value.inspect}
|
520
|
+
#log_debug("expand #{expanded_property}", depth: log_depth.to_i) { expanded_value.inspect}
|
476
521
|
output_object[expanded_property] = expanded_value unless expanded_value.nil? && expanded_property == '@value' && input_type != '@json'
|
477
522
|
next
|
478
523
|
end
|
@@ -481,16 +526,16 @@ module JSON::LD
|
|
481
526
|
expanded_value = if context.coerce(key) == '@json'
|
482
527
|
# In JSON-LD 1.1, values can be native JSON
|
483
528
|
{"@value" => value, "@type" => "@json"}
|
484
|
-
elsif container.
|
529
|
+
elsif container.include?('@language') && value.is_a?(Hash)
|
485
530
|
# Otherwise, if key's container mapping in active context is @language and value is a JSON object then value is expanded from a language map as follows:
|
486
531
|
|
487
532
|
# Set multilingual array to an empty array.
|
488
533
|
ary = []
|
489
534
|
|
490
535
|
# For each key-value pair language-language value in value, ordered lexicographically by language
|
491
|
-
keys = ordered ? value.keys.sort : value.keys
|
536
|
+
keys = @options[:ordered] ? value.keys.sort : value.keys
|
492
537
|
keys.each do |k|
|
493
|
-
expanded_k = context.expand_iri(k, vocab: true,
|
538
|
+
expanded_k = context.expand_iri(k, vocab: true, as_string: true, base: @options[:base])
|
494
539
|
|
495
540
|
if k !~ /^[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*$/ && expanded_k != '@none'
|
496
541
|
warn "@language must be valid BCP47: #{k.inspect}"
|
@@ -510,7 +555,7 @@ module JSON::LD
|
|
510
555
|
end
|
511
556
|
|
512
557
|
ary
|
513
|
-
elsif container.
|
558
|
+
elsif container.intersect?(CONTAINER_INDEX_ID_TYPE) && value.is_a?(Hash)
|
514
559
|
# Otherwise, if key's container mapping in active context contains @index, @id, @type and value is a JSON object then value is expanded from an index map as follows:
|
515
560
|
|
516
561
|
# Set ary to an empty array.
|
@@ -522,26 +567,38 @@ module JSON::LD
|
|
522
567
|
context.previous_context
|
523
568
|
elsif container.include?('@id') && context.term_definitions[key]
|
524
569
|
id_context = context.term_definitions[key].context if context.term_definitions[key]
|
525
|
-
id_context
|
570
|
+
if id_context.nil?
|
571
|
+
context
|
572
|
+
else
|
573
|
+
log_debug("expand", depth: log_depth.to_i) {"id_context: #{id_context.inspect}"}
|
574
|
+
context.parse(id_context, base: @options[:base], propagate: false)
|
575
|
+
end
|
526
576
|
else
|
527
577
|
context
|
528
578
|
end
|
529
579
|
|
530
580
|
# For each key-value in the object:
|
531
|
-
keys = ordered ? value.keys.sort : value.keys
|
581
|
+
keys = @options[:ordered] ? value.keys.sort : value.keys
|
532
582
|
keys.each do |k|
|
533
583
|
# If container mapping in the active context includes @type, and k is a term in the active context having a local context, use that context when expanding values
|
534
584
|
map_context = container_context.term_definitions[k].context if container.include?('@type') && container_context.term_definitions[k]
|
535
|
-
map_context
|
585
|
+
unless map_context.nil?
|
586
|
+
log_debug("expand", depth: log_depth.to_i) {"map_context: #{map_context.inspect}"}
|
587
|
+
map_context = container_context.parse(map_context, base: @options[:base],
|
588
|
+
propagate: false)
|
589
|
+
end
|
536
590
|
map_context ||= container_context
|
537
591
|
|
538
|
-
expanded_k = container_context.expand_iri(k, vocab: true,
|
592
|
+
expanded_k = container_context.expand_iri(k, vocab: true, as_string: true, base: @options[:base])
|
539
593
|
|
540
594
|
# Initialize index value to the result of using this algorithm recursively, passing active context, key as active property, and index value as element.
|
541
|
-
index_value = expand([value[k]].flatten, key, map_context,
|
595
|
+
index_value = expand([value[k]].flatten, key, map_context,
|
596
|
+
framing: framing,
|
597
|
+
from_map: true,
|
598
|
+
log_depth: log_depth.to_i + 1)
|
542
599
|
index_value.each do |item|
|
543
|
-
case
|
544
|
-
when
|
600
|
+
case
|
601
|
+
when container.include?('@index')
|
545
602
|
# Indexed graph by graph name
|
546
603
|
if !graph?(item) && container.include?('@graph')
|
547
604
|
item = {'@graph' => as_array(item)}
|
@@ -552,19 +609,19 @@ module JSON::LD
|
|
552
609
|
raise JsonLdError::InvalidValueObject, "Attempt to add illegal key to value object: #{index_key}"
|
553
610
|
else
|
554
611
|
# Expand key based on term
|
555
|
-
expanded_k = k == '@none' ? '@none' : container_context.expand_value(index_key, k)
|
556
|
-
index_property = container_context.expand_iri(index_key, vocab: true,
|
612
|
+
expanded_k = k == '@none' ? '@none' : container_context.expand_value(index_key, k, base: @options[:base])
|
613
|
+
index_property = container_context.expand_iri(index_key, vocab: true, as_string: true, base: @options[:base])
|
557
614
|
item[index_property] = [expanded_k].concat(Array(item[index_property])) unless expanded_k == '@none'
|
558
615
|
end
|
559
|
-
when
|
616
|
+
when container.include?('@id')
|
560
617
|
# Indexed graph by graph name
|
561
618
|
if !graph?(item) && container.include?('@graph')
|
562
619
|
item = {'@graph' => as_array(item)}
|
563
620
|
end
|
564
621
|
# Expand k document relative
|
565
|
-
expanded_k = container_context.expand_iri(k,
|
622
|
+
expanded_k = container_context.expand_iri(k, as_string: true, base: @options[:base], documentRelative: true) unless expanded_k == '@none'
|
566
623
|
item['@id'] ||= expanded_k unless expanded_k == '@none'
|
567
|
-
when
|
624
|
+
when container.include?('@type')
|
568
625
|
item['@type'] = [expanded_k].concat(Array(item['@type'])) unless expanded_k == '@none'
|
569
626
|
end
|
570
627
|
|
@@ -575,26 +632,28 @@ module JSON::LD
|
|
575
632
|
ary
|
576
633
|
else
|
577
634
|
# Otherwise, initialize expanded value to the result of using this algorithm recursively, passing active context, key for active property, and value for element.
|
578
|
-
expand(value, key, context,
|
635
|
+
expand(value, key, context,
|
636
|
+
framing: framing,
|
637
|
+
log_depth: log_depth.to_i + 1)
|
579
638
|
end
|
580
639
|
|
581
640
|
# If expanded value is null, ignore key by continuing to the next key from element.
|
582
641
|
if expanded_value.nil?
|
583
|
-
#log_debug(" => skip nil value")
|
642
|
+
#log_debug(" => skip nil value", depth: log_depth.to_i)
|
584
643
|
next
|
585
644
|
end
|
586
|
-
#log_debug {" => #{expanded_value.inspect}"}
|
645
|
+
#log_debug(depth: log_depth.to_i) {" => #{expanded_value.inspect}"}
|
587
646
|
|
588
647
|
# If the container mapping associated to key in active context is @list and expanded value is not already a list object, convert expanded value to a list object by first setting it to an array containing only expanded value if it is not already an array, and then by setting it to a JSON object containing the key-value pair @list-expanded value.
|
589
648
|
if container.first == '@list' && container.length == 1 && !list?(expanded_value)
|
590
|
-
#log_debug(" => ") { "convert #{expanded_value.inspect} to list"}
|
649
|
+
#log_debug(" => ", depth: log_depth.to_i) { "convert #{expanded_value.inspect} to list"}
|
591
650
|
expanded_value = {'@list' => as_array(expanded_value)}
|
592
651
|
end
|
593
|
-
#log_debug {" => #{expanded_value.inspect}"}
|
652
|
+
#log_debug(depth: log_depth.to_i) {" => #{expanded_value.inspect}"}
|
594
653
|
|
595
654
|
# convert expanded value to @graph if container specifies it
|
596
655
|
if container.first == '@graph' && container.length == 1
|
597
|
-
#log_debug(" => ") { "convert #{expanded_value.inspect} to list"}
|
656
|
+
#log_debug(" => ", depth: log_depth.to_i) { "convert #{expanded_value.inspect} to list"}
|
598
657
|
expanded_value = as_array(expanded_value).map do |v|
|
599
658
|
{'@graph' => as_array(v)}
|
600
659
|
end
|
@@ -630,16 +689,24 @@ module JSON::LD
|
|
630
689
|
|
631
690
|
# For each key in nests, recusively expand content
|
632
691
|
nests.each do |key|
|
692
|
+
nest_context = context.term_definitions[key].context if context.term_definitions[key]
|
693
|
+
nest_context = if nest_context.nil?
|
694
|
+
context
|
695
|
+
else
|
696
|
+
log_debug("expand", depth: log_depth.to_i) {"nest_context: #{nest_context.inspect}"}
|
697
|
+
context.parse(nest_context, base: @options[:base],
|
698
|
+
override_protected: true)
|
699
|
+
end
|
633
700
|
nested_values = as_array(input[key])
|
634
701
|
nested_values.each do |nv|
|
635
702
|
raise JsonLdError::InvalidNestValue, nv.inspect unless
|
636
|
-
nv.is_a?(Hash) && nv.keys.none? {|k|
|
637
|
-
expand_object(nv, active_property,
|
703
|
+
nv.is_a?(Hash) && nv.keys.none? {|k| nest_context.expand_iri(k, vocab: true, base: @options[:base]) == '@value'}
|
704
|
+
expand_object(nv, active_property, nest_context, output_object,
|
705
|
+
framing: framing,
|
638
706
|
expanded_active_property: expanded_active_property,
|
639
|
-
type_scoped_context: type_scoped_context,
|
640
707
|
type_key: type_key,
|
641
|
-
|
642
|
-
|
708
|
+
type_scoped_context: type_scoped_context,
|
709
|
+
log_depth: log_depth.to_i + 1)
|
643
710
|
end
|
644
711
|
end
|
645
712
|
end
|