json-ld 2.2.1 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -30,7 +30,8 @@ module JSON::LD
30
30
  # If element has a single member and the active property has no
31
31
  # @container mapping to @list or @set, the compacted value is that
32
32
  # member; otherwise the compacted value is element
33
- if result.length == 1 && !context.as_array?(property) && @options[:compactArrays]
33
+ if result.length == 1 &&
34
+ !context.as_array?(property) && @options[:compactArrays]
34
35
  #log_debug("=> extract single element: #{result.first.inspect}")
35
36
  result.first
36
37
  else
@@ -51,26 +52,34 @@ module JSON::LD
51
52
  end
52
53
  end
53
54
 
55
+ # If expanded property is @list and we're contained within a list container, recursively compact this item to an array
56
+ if list?(element) && context.container(property) == %w(@list)
57
+ return compact(element['@list'], property: property)
58
+ end
59
+
60
+
54
61
  inside_reverse = property == '@reverse'
55
62
  result, nest_result = {}, nil
56
63
 
64
+ # Apply any context defined on an alias of @type
65
+ # If key is @type and any compacted value is a term having a local context, overlay that context.
66
+ Array(element['@type']).
67
+ map {|expanded_type| context.compact_iri(expanded_type, vocab: true)}.
68
+ sort.
69
+ each do |term|
70
+ term_context = self.context.term_definitions[term].context if context.term_definitions[term]
71
+ self.context = context.parse(term_context) if term_context
72
+ end
73
+
57
74
  element.keys.sort.each do |expanded_property|
58
75
  expanded_value = element[expanded_property]
59
76
  #log_debug("") {"#{expanded_property}: #{expanded_value.inspect}"}
60
77
 
61
78
  if expanded_property == '@id' || expanded_property == '@type'
62
- compacted_value = [expanded_value].flatten.compact.map do |expanded_type|
79
+ compacted_value = Array(expanded_value).map do |expanded_type|
63
80
  context.compact_iri(expanded_type, vocab: (expanded_property == '@type'), log_depth: @options[:log_depth])
64
81
  end
65
82
 
66
- # If key is @type and any compacted value is a term having a local context, overlay that context.
67
- if expanded_property == '@type'
68
- compacted_value.each do |term|
69
- term_context = self.context.term_definitions[term].context if context.term_definitions[term]
70
- self.context = context.parse(term_context) if term_context
71
- end
72
- end
73
-
74
83
  compacted_value = compacted_value.first if compacted_value.length == 1
75
84
 
76
85
  al = context.compact_iri(expanded_property, vocab: true, quiet: true)
@@ -85,11 +94,8 @@ module JSON::LD
85
94
  # handle double-reversed properties
86
95
  compacted_value.each do |prop, value|
87
96
  if context.reverse?(prop)
88
- value = [value] if !value.is_a?(Array) &&
89
- (context.as_array?(prop) || !@options[:compactArrays])
90
- #log_debug("") {"merge #{prop} => #{value.inspect}"}
91
-
92
- merge_compacted_value(result, prop, value)
97
+ add_value(result, prop, value,
98
+ property_is_array: context.as_array?(prop) || !@options[:compactArrays])
93
99
  compacted_value.delete(prop)
94
100
  end
95
101
  end
@@ -136,11 +142,11 @@ module JSON::LD
136
142
 
137
143
  if nest_prop = context.nest(item_active_property)
138
144
  result[nest_prop] ||= {}
139
- iap = result[result[nest_prop]] ||= []
140
- result[nest_prop][item_active_property] = [iap] unless iap.is_a?(Array)
145
+ add_value(result[nest_prop], item_active_property, [],
146
+ property_is_array: true)
141
147
  else
142
- iap = result[item_active_property] ||= []
143
- result[item_active_property] = [iap] unless iap.is_a?(Array)
148
+ add_value(result, item_active_property, [],
149
+ property_is_array: true)
144
150
  end
145
151
  end
146
152
 
@@ -162,7 +168,7 @@ module JSON::LD
162
168
  end
163
169
 
164
170
  container = context.container(item_active_property)
165
- as_array = context.as_array?(item_active_property)
171
+ as_array = !@options[:compactArrays] || context.as_array?(item_active_property)
166
172
 
167
173
  value = case
168
174
  when list?(expanded_item) then expanded_item['@list']
@@ -175,7 +181,7 @@ module JSON::LD
175
181
 
176
182
  # handle @list
177
183
  if list?(expanded_item)
178
- compacted_item = [compacted_item] unless compacted_item.is_a?(Array)
184
+ compacted_item = as_array(compacted_item)
179
185
  unless container == %w(@list)
180
186
  al = context.compact_iri('@list', vocab: true, quiet: true)
181
187
  compacted_item = {al => compacted_item}
@@ -184,36 +190,35 @@ module JSON::LD
184
190
  compacted_item[key] = expanded_item['@index']
185
191
  end
186
192
  else
187
- raise JsonLdError::CompactionToListOfLists,
188
- "key cannot have more than one list value" if nest_result.has_key?(item_active_property)
189
- # Falls through to add list value below
193
+ add_value(nest_result, item_active_property, compacted_item,
194
+ value_is_array: true, allow_duplicate: true)
195
+ next
190
196
  end
191
197
  end
192
198
 
193
199
  # Graph object compaction cases:
194
200
  if graph?(expanded_item)
195
- if container.include?('@graph') && container.include?('@id')
201
+ if container.include?('@graph') &&
202
+ (container.include?('@id') || container.include?('@index') && simple_graph?(expanded_item))
196
203
  # container includes @graph and @id
197
204
  map_object = nest_result[item_active_property] ||= {}
198
- map_key = expanded_item['@id']
199
205
  # If there is no @id, create a blank node identifier to use as an index
200
- map_key = map_key ? context.compact_iri(map_key, quiet: true) : namer.get_name
201
- merge_compacted_value(map_object, map_key, compacted_item)
202
- elsif container.include?('@graph') && container.include?('@index') && simple_graph?(expanded_item)
203
- # container includes @graph and @index and value is a simple graph object
204
- map_object = nest_result[item_active_property] ||= {}
205
- # If there is no @index, use @none
206
- map_key = expanded_item['@index'] || '@none'
207
- merge_compacted_value(map_object, map_key, compacted_item)
206
+ map_key = if container.include?('@id') && expanded_item['@id']
207
+ context.compact_iri(expanded_item['@id'], quiet: true)
208
+ elsif container.include?('@index') && expanded_item['@index']
209
+ context.compact_iri(expanded_item['@index'], quiet: true)
210
+ else
211
+ context.compact_iri('@none', vocab: true, quiet: true)
212
+ end
213
+ add_value(map_object, map_key, compacted_item,
214
+ property_is_array: as_array)
208
215
  elsif container.include?('@graph') && simple_graph?(expanded_item)
209
216
  # container includes @graph but not @id or @index and value is a simple graph object
210
217
  # Drop through, where compacted_value will be added
211
- compacted_item = [compacted_item] if
212
- !compacted_item.is_a?(Array) && (!@options[:compactArrays] || as_array)
213
- merge_compacted_value(nest_result, item_active_property, compacted_item)
218
+ add_value(nest_result, item_active_property, compacted_item,
219
+ property_is_array: as_array)
214
220
  else
215
221
  # container does not include @graph or otherwise does not match one of the previous cases, redo compacted_item
216
- compacted_item = [compacted_item]
217
222
  al = context.compact_iri('@graph', vocab: true, quiet: true)
218
223
  compacted_item = {al => compacted_item}
219
224
  if expanded_item['@id']
@@ -224,47 +229,46 @@ module JSON::LD
224
229
  key = context.compact_iri('@index', vocab: true, quiet: true)
225
230
  compacted_item[key] = expanded_item['@index']
226
231
  end
227
- compacted_item = [compacted_item] if !@options[:compactArrays] || as_array
228
- merge_compacted_value(nest_result, item_active_property, compacted_item)
232
+ add_value(nest_result, item_active_property, compacted_item,
233
+ property_is_array: as_array)
229
234
  end
230
235
  elsif !(container & %w(@language @index @id @type)).empty? && !container.include?('@graph')
231
236
  map_object = nest_result[item_active_property] ||= {}
237
+ c = container.first
238
+ container_key = context.compact_iri(c, vocab: true, quiet: true)
232
239
  compacted_item = case container
233
240
  when %w(@id)
234
- id_prop = context.compact_iri('@id', vocab: true, quiet: true)
235
- map_key = compacted_item[id_prop]
236
- map_key = context.compact_iri(map_key, quiet: true)
237
- compacted_item.delete(id_prop)
241
+ map_key = compacted_item[container_key]
242
+ compacted_item.delete(container_key)
238
243
  compacted_item
239
244
  when %w(@index)
240
245
  map_key = expanded_item['@index']
246
+ compacted_item.delete(container_key) if compacted_item.is_a?(Hash)
241
247
  compacted_item
242
248
  when %w(@language)
243
249
  map_key = expanded_item['@language']
244
250
  value?(expanded_item) ? expanded_item['@value'] : compacted_item
245
251
  when %w(@type)
246
- type_prop = context.compact_iri('@type', vocab: true, quiet: true)
247
- map_key, *types = Array(compacted_item[type_prop])
248
- map_key = context.compact_iri(map_key, vocab: true, quiet: true)
252
+ map_key, *types = Array(compacted_item[container_key])
249
253
  case types.length
250
- when 0 then compacted_item.delete(type_prop)
251
- when 1 then compacted_item[type_prop] = types.first
252
- else compacted_item[type_prop] = types
254
+ when 0 then compacted_item.delete(container_key)
255
+ when 1 then compacted_item[container_key] = types.first
256
+ else compacted_item[container_key] = types
253
257
  end
254
258
  compacted_item
255
259
  end
256
- compacted_item = [compacted_item] if as_array && !compacted_item.is_a?(Array)
257
- merge_compacted_value(map_object, map_key, compacted_item)
260
+ map_key ||= context.compact_iri('@none', vocab: true, quiet: true)
261
+ add_value(map_object, map_key, compacted_item,
262
+ property_is_array: as_array)
258
263
  else
259
- compacted_item = [compacted_item] if
260
- !compacted_item.is_a?(Array) && (!@options[:compactArrays] || as_array)
261
- merge_compacted_value(nest_result, item_active_property, compacted_item)
264
+ add_value(nest_result, item_active_property, compacted_item,
265
+ property_is_array: as_array)
262
266
  end
263
267
  end
264
268
  end
265
269
 
266
270
  # Re-order result keys
267
- result.keys.kw_sort.each_with_object({}) {|kk, memo| memo[kk] = result[kk]}
271
+ result.keys.sort.each_with_object({}) {|kk, memo| memo[kk] = result[kk]}
268
272
  else
269
273
  # For other types, the compacted value is the element value
270
274
  #log_debug("compact") {element.class.to_s}
@@ -336,15 +336,15 @@ module JSON::LD
336
336
  end
337
337
 
338
338
  # If contex has a @version member, it's value MUST be 1.1, otherwise an "invalid @version value" has been detected, and processing is aborted.
339
- # If processingMode has been set, and "json-ld-1.1" is not a prefix of processingMode , a "processing mode conflict" has been detecting, and processing is aborted.
339
+ # If processingMode has been set, and it is not "json-ld-1.1", a "processing mode conflict" has been detecting, and processing is aborted.
340
340
  # @param [Number] vaule must be a decimal number
341
341
  def version=(value)
342
342
  case value
343
343
  when 1.1
344
- if processingMode && !processingMode.start_with?("json-ld-1.1")
344
+ if processingMode && processingMode < "json-ld-1.1"
345
345
  raise JsonLdError::ProcessingModeConflict, "#{value} not compatible with #{processingMode}"
346
346
  end
347
- @processingMode ||= "json-ld-1.1"
347
+ @processingMode = "json-ld-1.1"
348
348
  else
349
349
  raise JsonLdError::InvalidVersionValue, value
350
350
  end
@@ -357,13 +357,13 @@ module JSON::LD
357
357
  when /_:/
358
358
  value
359
359
  when String, RDF::URI
360
- v = as_resource(value.to_s)
361
- raise JsonLdError::InvalidVocabMapping, "@vocab must be an absolute IRI: #{value.inspect}" if v.uri? && v.relative? && @options[:validate]
360
+ v = as_resource(value.to_s, base)
361
+ raise JsonLdError::InvalidVocabMapping, "@vocab must be an IRI: #{value.inspect}" if !v.valid? && @options[:validate]
362
362
  v
363
363
  when nil
364
364
  nil
365
365
  else
366
- raise JsonLdError::InvalidVocabMapping, "@vocab must be an absolute IRI: #{value.inspect}"
366
+ raise JsonLdError::InvalidVocabMapping, "@vocab must be an IRI: #{value.inspect}"
367
367
  end
368
368
  end
369
369
 
@@ -385,7 +385,7 @@ module JSON::LD
385
385
  result = self.dup
386
386
  result.provided_context = local_context if self.empty?
387
387
 
388
- local_context = [local_context] unless local_context.is_a?(Array)
388
+ local_context = as_array(local_context)
389
389
 
390
390
  local_context.each do |context|
391
391
  case context
@@ -419,6 +419,7 @@ module JSON::LD
419
419
 
420
420
  raise JsonLdError::RecursiveContextInclusion, "#{context}" if remote_contexts.include?(context.to_s)
421
421
  remote_contexts << context.to_s
422
+ raise JsonLdError::ContextOverflow, "#{context}" if remote_contexts.length >= MAX_CONTEXTS_LOADED
422
423
 
423
424
  context_no_base = result.dup
424
425
  context_no_base.base = nil
@@ -448,9 +449,6 @@ module JSON::LD
448
449
  end
449
450
  raise JsonLdError::InvalidRemoteContext, "#{context}" unless jo.is_a?(Hash) && jo.has_key?('@context')
450
451
  context = jo['@context']
451
- if (processingMode || 'json-ld-1.0') <= "json-ld-1.1"
452
- context_no_base.provided_context = context.dup
453
- end
454
452
  end
455
453
  rescue JsonLdError::LoadingDocumentFailed => e
456
454
  #log_debug("parse") {"Failed to retrieve @context from remote document at #{context_no_base.context_base.inspect}: #{e.message}"}
@@ -464,6 +462,7 @@ module JSON::LD
464
462
 
465
463
  # 3.2.6) Set context to the result of recursively calling this algorithm, passing context no base for active context, context for local context, and remote contexts.
466
464
  context = context_no_base.parse(context, remote_contexts.dup)
465
+ PRELOADED[context_canon.to_s] = context
467
466
  context.provided_context = result.provided_context
468
467
  end
469
468
  context.base ||= result.base
@@ -486,11 +485,9 @@ module JSON::LD
486
485
  end
487
486
  end
488
487
 
489
- # If not set explicitly, set processingMode to "json-ld-1.0"
490
- result.processingMode ||= "json-ld-1.0"
491
-
492
488
  defined = {}
493
- # For each key-value pair in context invoke the Create Term Definition subalgorithm, passing result for active context, context for local context, key, and defined
489
+
490
+ # For each key-value pair in context invoke the Create Term Definition subalgorithm, passing result for active context, context for local context, key, and defined
494
491
  context.each_key do |key|
495
492
  result.create_term_definition(context, key, defined)
496
493
  end
@@ -651,10 +648,10 @@ module JSON::LD
651
648
  raise JsonLdError::InvalidKeywordAlias, "expected value of @id to not be @context on term #{term.inspect}" if
652
649
  definition.id == '@context'
653
650
 
654
- # If id ends with a gen-delim, it may be used as a prefix
655
- definition.prefix = true if !term.include?(':') &&
656
- definition.id.to_s.end_with?(*%w(: / ? # [ ] @)) &&
657
- (simple_term || ((processingMode || 'json-ld-1.0') == 'json-ld-1.0'))
651
+ # If id ends with a gen-delim, it may be used as a prefix for simple terms
652
+ definition.prefix = true if !term.include?(':') &&
653
+ definition.id.to_s.end_with?(*%w(: / ? # [ ] @)) &&
654
+ simple_term
658
655
  elsif term.include?(':')
659
656
  # If term is a compact IRI with a prefix that is a key in local context then a dependency has been found. Use this algorithm recursively passing active context, local context, the prefix as term, and defined.
660
657
  prefix, suffix = term.split(':', 2)
@@ -735,7 +732,7 @@ module JSON::LD
735
732
  #
736
733
  # @param [Hash{Symbol => Object}] options ({})
737
734
  # @return [Hash]
738
- def serialize(options = {})
735
+ def serialize(**options)
739
736
  # FIXME: not setting provided_context now
740
737
  use_context = case provided_context
741
738
  when String, RDF::URI
@@ -994,8 +991,14 @@ module JSON::LD
994
991
  create_term_definition(local_context, value, defined)
995
992
  end
996
993
 
994
+ if (v_td = term_definitions[value]) && KEYWORDS.include?(v_td.id)
995
+ #log_debug("") {"match with #{v_td.id}"} unless quiet
996
+ return v_td.id
997
+ end
998
+
999
+ # If active context has a term definition for value, and the associated mapping is a keyword, return that keyword.
997
1000
  # If vocab is true and the active context has a term definition for value, return the associated IRI mapping.
998
- if vocab && (v_td = term_definitions[value])
1001
+ if (v_td = term_definitions[value]) && (vocab || KEYWORDS.include?(v_td.id))
999
1002
  #log_debug("") {"match with #{v_td.id}"} unless quiet
1000
1003
  return v_td.id
1001
1004
  end
@@ -1030,7 +1033,7 @@ module JSON::LD
1030
1033
  result = if vocab && self.vocab
1031
1034
  # If vocab is true, and active context has a vocabulary mapping, return the result of concatenating the vocabulary mapping with value.
1032
1035
  self.vocab + value
1033
- elsif documentRelative && (base ||= self.base)
1036
+ elsif (documentRelative || self.vocab == '') && (base ||= self.base)
1034
1037
  # Otherwise, if document relative is true, set value to the result of resolving value against the base IRI. Only the basic algorithm in section 5.2 of [RFC3986] is used; neither Syntax-Based Normalization nor Scheme-Based Normalization are performed. Characters additionally allowed in IRI references are treated in the same way that unreserved characters are treated in URI references, per section 6.5 of [RFC3987].
1035
1038
  value = RDF::URI(value)
1036
1039
  value.absolute? ? value : RDF::URI(base).join(value)
@@ -1069,15 +1072,11 @@ module JSON::LD
1069
1072
  default_language = self.default_language || "@none"
1070
1073
  containers = []
1071
1074
  tl, tl_value = "@language", "@null"
1075
+ containers.concat(%w(@index @index@set)) if index?(value) && !graph?(value)
1072
1076
 
1073
1077
  # If the value is a JSON Object with the key @preserve, use the value of @preserve.
1074
1078
  value = value['@preserve'].first if value.is_a?(Hash) && value.has_key?('@preserve')
1075
1079
 
1076
- # If the value is a JSON Object, then for the keywords @index, @id, and @type, if the value contains that keyword, append it to containers.
1077
- %w(@index @id @type).each do |kw|
1078
- containers << kw if value.has_key?(kw)
1079
- end if value.is_a?(Hash)
1080
-
1081
1080
  if reverse
1082
1081
  tl, tl_value = "@type", "@reverse"
1083
1082
  containers << '@set'
@@ -1123,20 +1122,30 @@ module JSON::LD
1123
1122
  end
1124
1123
  #log_debug("") {"list: containers: #{containers.inspect}, type/language: #{tl.inspect}, type/language value: #{tl_value.inspect}"} unless quiet
1125
1124
  elsif graph?(value)
1126
- # TODO: support `@graphId`?
1127
- # TODO: "@graph@set"?
1128
- containers << '@graph'
1129
- containers << '@set'
1125
+ # Prefer @index and @id containers, then @graph, then @index
1126
+ containers.concat(%w(@graph@index @graph@index@set @index @index@set)) if index?(value)
1127
+ containers.concat(%w(@graph@id @graph@id@set)) if value.has_key?('@id')
1128
+
1129
+ # Prefer an @graph container next
1130
+ containers.concat(%w(@graph @graph@set @set))
1131
+
1132
+ # Lastly, in 1.1, any graph can be indexed on @index or @id, so add if we haven't already
1133
+ containers.concat(%w(@graph@index @graph@index@set)) unless index?(value)
1134
+ containers.concat(%w(@graph@id @graph@id@set)) unless value.has_key?('@id')
1135
+ containers.concat(%w(@index @index@set)) unless index?(value)
1130
1136
  else
1131
1137
  if value?(value)
1138
+ # In 1.1, an language map can be used to index values using @none
1132
1139
  if value.has_key?('@language') && !index?(value)
1133
1140
  tl_value = value['@language']
1134
- containers << '@language'
1141
+ containers.concat(%w(@language @language@set))
1135
1142
  elsif value.has_key?('@type')
1136
1143
  tl_value = value['@type']
1137
1144
  tl = '@type'
1138
1145
  end
1139
1146
  else
1147
+ # In 1.1, an id or type map can be used to index values using @none
1148
+ containers.concat(%w(@id @id@set @type @set@type))
1140
1149
  tl, tl_value = '@type', '@id'
1141
1150
  end
1142
1151
  containers << '@set'
@@ -1144,6 +1153,12 @@ module JSON::LD
1144
1153
  end
1145
1154
 
1146
1155
  containers << '@none'
1156
+
1157
+ # In 1.1, an index map can be used to index values using @none, so add as a low priority
1158
+ containers.concat(%w(@index @index@set)) unless index?(value)
1159
+ # Values without type or language can use @language map
1160
+ containers.concat(%w(@language @language@set)) if value?(value) && value.keys == %w(@value)
1161
+
1147
1162
  tl_value ||= '@null'
1148
1163
  preferred_values = []
1149
1164
  preferred_values << '@reverse' if tl_value == '@reverse'
@@ -1306,7 +1321,7 @@ module JSON::LD
1306
1321
  # @raise [JsonLdError] if the iri cannot be expanded
1307
1322
  # @see http://json-ld.org/spec/latest/json-ld-api/#value-compaction
1308
1323
  # FIXME: revisit the specification version of this.
1309
- def compact_value(property, value, options = {})
1324
+ def compact_value(property, value, **options)
1310
1325
  #log_debug("compact_value") {"property: #{property.inspect}, value: #{value.inspect}"}
1311
1326
 
1312
1327
  num_members = value.length
@@ -1521,14 +1536,11 @@ module JSON::LD
1521
1536
  end.each do |term|
1522
1537
  next unless td = term_definitions[term]
1523
1538
 
1524
- container = Array(td.container_mapping).sort.first
1525
- container ||= td.as_set? ? %(@set) : %(@none)
1526
- # FIXME: Alternative to consider
1527
- ## Creates "@language", "@language@set", "@set", or "@none"
1528
- ## for each of "@language", "@index", "@type", "@id", "@list", and "@graph"
1529
- #container = td.container_mapping.to_s
1530
- #container += '@set' if td.as_set?
1531
- #container = '@none' if container.empty?
1539
+ container = td.container_mapping.join('')
1540
+ if container.empty?
1541
+ container = td.as_set? ? %(@set) : %(@none)
1542
+ end
1543
+
1532
1544
  container_map = result[td.id.to_s] ||= {}
1533
1545
  tl_map = container_map[container] ||= {'@language' => {}, '@type' => {}, '@any' => {}}
1534
1546
  type_map = tl_map['@type']
@@ -16,22 +16,23 @@ module JSON::LD
16
16
  # @param [Context] context
17
17
  # @param [Boolean] ordered (true)
18
18
  # Ensure output objects have keys ordered properly
19
+ # @param [Boolean] framing (false)
20
+ # Special rules for expanding a frame
19
21
  # @return [Array<Hash{String => Object}>]
20
- def expand(input, active_property, context, ordered: true)
21
- framing = @options[:processingMode].include?("expand-frame")
22
+ def expand(input, active_property, context, ordered: true, framing: false)
22
23
  #log_debug("expand") {"input: #{input.inspect}, active_property: #{active_property.inspect}, context: #{context.inspect}"}
24
+ framing = false if active_property == '@default'
23
25
  result = case input
24
26
  when Array
25
27
  # If element is an array,
26
28
  is_list = context.container(active_property) == %w(@list)
27
29
  value = input.each_with_object([]) do |v, memo|
28
30
  # Initialize expanded item to the result of using this algorithm recursively, passing active context, active property, and item as element.
29
- v = expand(v, active_property, context, ordered: ordered)
31
+ v = expand(v, active_property, context, ordered: ordered, framing: framing)
32
+
33
+ # 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
34
+ v = {"@list" => v} if is_list && v.is_a?(Array)
30
35
 
31
- # If the active property is @list or its container mapping is set to @list, the expanded item must not be an array or a list object, otherwise a list of lists error has been detected and processing is aborted.
32
- raise JsonLdError::ListOfLists,
33
- "A list may not contain another list" if
34
- is_list && (v.is_a?(Array) || list?(v))
35
36
  case v
36
37
  when nil then nil
37
38
  when Array then memo.concat(v)
@@ -52,14 +53,14 @@ module JSON::LD
52
53
  # See if keys mapping to @type have terms with a local context
53
54
  input.each_pair do |key, val|
54
55
  next unless context.expand_iri(key, vocab: true) == '@type'
55
- Array(val).each do |term|
56
+ Array(val).sort.each do |term|
56
57
  term_context = context.term_definitions[term].context if context.term_definitions[term]
57
58
  context = term_context ? context.parse(term_context) : context
58
59
  end
59
60
  end
60
61
 
61
62
  # Process each key and value in element. Ignores @nesting content
62
- expand_object(input, active_property, context, output_object, ordered: ordered)
63
+ expand_object(input, active_property, context, output_object, ordered: ordered, framing: framing)
63
64
 
64
65
  #log_debug("output object") {output_object.inspect}
65
66
 
@@ -83,8 +84,8 @@ module JSON::LD
83
84
  # 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.
84
85
  raise JsonLdError::InvalidLanguageTaggedValue,
85
86
  "when @language is used, @value must be a string: #{output_object.inspect}"
86
- elsif !Array(output_object.fetch('@type', "")).all? {|t|
87
- t.is_a?(String) && context.expand_iri(t, vocab: true, log_depth: @options[:log_depth]).is_a?(RDF::URI) ||
87
+ elsif !Array(output_object['@type']).all? {|t|
88
+ t.is_a?(String) && RDF::URI(t).absolute? && !t.start_with?('_:') ||
88
89
  t.is_a?(Hash) && t.empty?}
89
90
  # 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.
90
91
  raise JsonLdError::InvalidTypedValue,
@@ -117,7 +118,7 @@ module JSON::LD
117
118
 
118
119
  # Re-order result keys if ordering
119
120
  if ordered
120
- output_object.keys.kw_sort.each_with_object({}) {|kk, memo| memo[kk] = output_object[kk]}
121
+ output_object.keys.sort.each_with_object({}) {|kk, memo| memo[kk] = output_object[kk]}
121
122
  else
122
123
  output_object
123
124
  end
@@ -135,12 +136,11 @@ module JSON::LD
135
136
  CONTAINER_MAPPING_INDEX_ID_TYPE = Set.new(%w(@index @id @type)).freeze
136
137
 
137
138
  # Expand each key and value of element adding them to result
138
- def expand_object(input, active_property, context, output_object, ordered: false)
139
- framing = @options[:processingMode].include?("expand-frame")
139
+ def expand_object(input, active_property, context, output_object, ordered:, framing:)
140
140
  nests = []
141
141
 
142
142
  # Then, proceed and process each property and value in element as follows:
143
- keys = ordered ? input.keys.kw_sort : input.keys
143
+ keys = ordered ? input.keys.sort : input.keys
144
144
  keys.each do |key|
145
145
  # For each key and value in element, ordered lexicographically by key:
146
146
  value = input[key]
@@ -195,8 +195,8 @@ module JSON::LD
195
195
  end
196
196
 
197
197
  # Use array form if framing
198
- if framing && !e_id.is_a?(Array)
199
- [e_id]
198
+ if framing
199
+ as_array(e_id)
200
200
  else
201
201
  e_id
202
202
  end
@@ -224,8 +224,8 @@ module JSON::LD
224
224
  end
225
225
  when '@graph'
226
226
  # 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.
227
- value = expand(value, '@graph', context, ordered: ordered)
228
- value.is_a?(Array) ? value : [value]
227
+ value = expand(value, '@graph', context, ordered: ordered, framing: framing)
228
+ as_array(value)
229
229
  when '@value'
230
230
  # If expanded property is @value and value is not a scalar or null, an invalid value object value error has been detected and processing is aborted. Otherwise, set expanded value to value. If expanded value is null, set the @value member of result to null and continue with the next key from element. Null values need to be preserved in this case as the meaning of an @type member depends on the existence of an @value member.
231
231
  # If framing, always use array form, unless null
@@ -277,20 +277,15 @@ module JSON::LD
277
277
  next if (active_property || '@graph') == '@graph'
278
278
 
279
279
  # Otherwise, initialize expanded value to the result of using this algorithm recursively passing active context, active property, and value for element.
280
- value = expand(value, active_property, context, ordered: ordered)
280
+ value = expand(value, active_property, context, ordered: ordered, framing: framing)
281
281
 
282
282
  # Spec FIXME: need to be sure that result is an array
283
- value = [value] unless value.is_a?(Array)
284
-
285
- # If expanded value is a list object, a list of lists error has been detected and processing is aborted.
286
- # Spec FIXME: Also look at each object if result is an array
287
- raise JsonLdError::ListOfLists,
288
- "A list may not contain another list" if value.any? {|v| list?(v)}
283
+ value = as_array(value)
289
284
 
290
285
  value
291
286
  when '@set'
292
287
  # 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.
293
- expand(value, active_property, context, ordered: ordered)
288
+ expand(value, active_property, context, ordered: ordered, framing: framing)
294
289
  when '@reverse'
295
290
  # If expanded property is @reverse and value is not a JSON object, an invalid @reverse value error has been detected and processing is aborted.
296
291
  raise JsonLdError::InvalidReverseValue,
@@ -298,7 +293,7 @@ module JSON::LD
298
293
 
299
294
  # Otherwise
300
295
  # Initialize expanded value to the result of using this algorithm recursively, passing active context, @reverse as active property, and value as element.
301
- value = expand(value, '@reverse', context, ordered: ordered)
296
+ value = expand(value, '@reverse', context, ordered: ordered, framing: framing)
302
297
 
303
298
  # 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:
304
299
  if value.has_key?('@reverse')
@@ -331,7 +326,7 @@ module JSON::LD
331
326
  when '@default', '@embed', '@explicit', '@omitDefault', '@preserve', '@requireAll'
332
327
  next unless framing
333
328
  # Framing keywords
334
- [expand(value, expanded_property, context, ordered: ordered)].flatten
329
+ [expand(value, expanded_property, context, ordered: ordered, framing: framing)].flatten
335
330
  when '@nest'
336
331
  # Add key to nests
337
332
  nests << key
@@ -361,16 +356,16 @@ module JSON::LD
361
356
  # For each key-value pair language-language value in value, ordered lexicographically by language
362
357
  keys = ordered ? value.keys.sort : value.keys
363
358
  keys.each do |k|
359
+ expanded_k = active_context.expand_iri(k, vocab: true, quiet: true).to_s
364
360
  [value[k]].flatten.each do |item|
365
361
  # item must be a string, otherwise an invalid language map value error has been detected and processing is aborted.
366
362
  raise JsonLdError::InvalidLanguageMapValue,
367
363
  "Expected #{item.inspect} to be a string" unless item.nil? || item.is_a?(String)
368
364
 
369
365
  # Append a JSON object to expanded value that consists of two key-value pairs: (@value-item) and (@language-lowercased language).
370
- ary << {
371
- '@value' => item,
372
- '@language' => k.downcase
373
- } if item
366
+ v = {'@value' => item}
367
+ v['@language'] = k.downcase unless expanded_k == '@none'
368
+ ary << v if item
374
369
  end
375
370
  end
376
371
 
@@ -388,30 +383,29 @@ module JSON::LD
388
383
  map_context = active_context.term_definitions[k].context if container.include?('@type') && active_context.term_definitions[k]
389
384
  map_context = active_context.parse(map_context) if map_context
390
385
  map_context ||= active_context
391
-
386
+
387
+ expanded_k = active_context.expand_iri(k, vocab: true, quiet: true).to_s
388
+
392
389
  # Initialize index value to the result of using this algorithm recursively, passing active context, key as active property, and index value as element.
393
- index_value = expand([value[k]].flatten, key, map_context, ordered: ordered)
390
+ index_value = expand([value[k]].flatten, key, map_context, ordered: ordered, framing: framing)
394
391
  index_value.each do |item|
395
392
  case container
396
- when %w(@index) then item['@index'] ||= k
397
- when %w(@id)
398
- # Expand k document relative
399
- expanded_k = active_context.expand_iri(k, documentRelative: true, quiet: true).to_s
400
- item['@id'] ||= expanded_k
401
- when %w(@type)
402
- # Expand k vocabulary relative
403
- expanded_k = active_context.expand_iri(k, vocab: true, documentRelative: true, quiet: true).to_s
404
- item['@type'] = [expanded_k].concat(Array(item['@type']))
405
- when %w(@graph @index), %w(@graph @id)
393
+ when %w(@graph @index), %w(@index)
394
+ # Indexed graph by graph name
395
+ if !graph?(item) && container.include?('@graph')
396
+ item = {'@graph' => as_array(item)}
397
+ end
398
+ item['@index'] ||= k unless expanded_k == '@none'
399
+ when %w(@graph @id), %w(@id)
406
400
  # Indexed graph by graph name
407
- if !graph?(item)
408
- item = [item] unless expanded_value.is_a?(Array)
409
- item = {'@graph' => item}
401
+ if !graph?(item) && container.include?('@graph')
402
+ item = {'@graph' => as_array(item)}
410
403
  end
411
- expanded_k = container.include?('@index') ? k :
412
- active_context.expand_iri(k, documentRelative: true, quiet: true).to_s
413
404
  # Expand k document relative
414
- item[container.include?('@index') ? '@index' : '@id'] ||= k
405
+ expanded_k = active_context.expand_iri(k, documentRelative: true, quiet: true).to_s unless expanded_k == '@none'
406
+ item['@id'] ||= expanded_k unless expanded_k == '@none'
407
+ when %w(@type)
408
+ item['@type'] = [expanded_k].concat(Array(item['@type'])) unless expanded_k == '@none'
415
409
  end
416
410
 
417
411
  # Append item to expanded value.
@@ -421,7 +415,7 @@ module JSON::LD
421
415
  ary
422
416
  else
423
417
  # Otherwise, initialize expanded value to the result of using this algorithm recursively, passing active context, key for active property, and value for element.
424
- expand(value, key, active_context, ordered: ordered)
418
+ expand(value, key, active_context, ordered: ordered, framing: framing)
425
419
  end
426
420
 
427
421
  # If expanded value is null, ignore key by continuing to the next key from element.
@@ -434,17 +428,16 @@ module JSON::LD
434
428
  # 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.
435
429
  if active_context.container(key) == %w(@list) && !list?(expanded_value)
436
430
  #log_debug(" => ") { "convert #{expanded_value.inspect} to list"}
437
- expanded_value = [expanded_value] unless expanded_value.is_a?(Array)
438
- expanded_value = {'@list' => expanded_value}
431
+ expanded_value = {'@list' => as_array(expanded_value)}
439
432
  end
440
433
  #log_debug {" => #{expanded_value.inspect}"}
441
434
 
442
435
  # convert expanded value to @graph if container specifies it
443
- # FIXME value may be a named graph, as well as a simple graph.
444
- if active_context.container(key) == %w(@graph) && !graph?(expanded_value)
436
+ if active_context.container(key) == %w(@graph)
445
437
  #log_debug(" => ") { "convert #{expanded_value.inspect} to list"}
446
- expanded_value = [expanded_value] unless expanded_value.is_a?(Array)
447
- expanded_value = {'@graph' => expanded_value}
438
+ expanded_value = as_array(expanded_value).map do |v|
439
+ graph?(v) ? v : {'@graph' => as_array(v)}
440
+ end
448
441
  end
449
442
 
450
443
  # Otherwise, if the term definition associated to key indicates that it is a reverse property
@@ -470,12 +463,11 @@ module JSON::LD
470
463
 
471
464
  # For each key in nests, recusively expand content
472
465
  nests.each do |key|
473
- nested_values = input[key]
474
- nested_values = [input[key]] unless input[key].is_a?(Array)
466
+ nested_values = as_array(input[key])
475
467
  nested_values.each do |nv|
476
468
  raise JsonLdError::InvalidNestValue, nv.inspect unless
477
469
  nv.is_a?(Hash) && nv.keys.none? {|k| context.expand_iri(k, vocab: true) == '@value'}
478
- expand_object(nv, active_property, context, output_object, ordered: ordered)
470
+ expand_object(nv, active_property, context, output_object, ordered: ordered, framing: framing)
479
471
  end
480
472
  end
481
473
  end