json-ld 3.1.0 → 3.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/lib/json/ld.rb +1 -1
- data/lib/json/ld/api.rb +12 -12
- data/lib/json/ld/compact.rb +26 -33
- data/lib/json/ld/context.rb +54 -47
- data/lib/json/ld/expand.rb +27 -32
- data/lib/json/ld/frame.rb +1 -1
- data/lib/json/ld/from_rdf.rb +1 -1
- data/lib/json/ld/to_rdf.rb +9 -3
- data/lib/json/ld/writer.rb +2 -2
- data/spec/context_spec.rb +43 -43
- data/spec/suite_expand_spec.rb +2 -0
- data/spec/suite_helper.rb +5 -0
- data/spec/to_rdf_spec.rb +3 -3
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 686aaec1dba1b51a5f37e1d4ebd28395c189c0944022b1b9877c7c5a8500cef3
|
4
|
+
data.tar.gz: 5a9f07dd8fc17e3b4b3129b45cbd783e5390b7921da10c62d7d087621e1e123d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f3bb2f7b57db9b1689e43ceca7fbe497f0c67fffd5c12a8a7b451f07e0acf45be1f45f30d3acfd2e7e5ab1a52b9486954e8af1ebceb40478599b1fa8d9809035
|
7
|
+
data.tar.gz: 8acfd478567cec305f2a37e189a7ba11c2bb75463746bc1a114be79b71ea3e84b19632224515b0e2097c2a2a16293f098eb06556d74bb6a2a7e32516ad1476d3
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
3.1.
|
1
|
+
3.1.1
|
data/lib/json/ld.rb
CHANGED
@@ -115,7 +115,7 @@ module JSON
|
|
115
115
|
class CyclicIRIMapping < JsonLdError; @code = "cyclic IRI mapping"; end
|
116
116
|
class InvalidBaseIRI < JsonLdError; @code = "invalid base IRI"; end
|
117
117
|
class InvalidContainerMapping < JsonLdError; @code = "invalid container mapping"; end
|
118
|
-
class
|
118
|
+
class InvalidContextEntry < JsonLdError; @code = "invalid context entry"; end
|
119
119
|
class InvalidContextNullification < JsonLdError; @code = "invalid context nullification"; end
|
120
120
|
class InvalidDefaultLanguage < JsonLdError; @code = "invalid default language"; end
|
121
121
|
class InvalidIdValue < JsonLdError; @code = "invalid @id value"; end
|
data/lib/json/ld/api.rb
CHANGED
@@ -35,7 +35,7 @@ module JSON::LD
|
|
35
35
|
|
36
36
|
# Options used for open_file
|
37
37
|
OPEN_OPTS = {
|
38
|
-
headers: {"Accept" => "application/ld+json, text/html;q=0.8, application/json;q=0.5"}
|
38
|
+
headers: {"Accept" => "application/ld+json, text/html;q=0.8, application/xhtml+xml;q=0.8, application/json;q=0.5"}
|
39
39
|
}
|
40
40
|
|
41
41
|
# The following constants are used to reduce object allocations
|
@@ -229,7 +229,7 @@ module JSON::LD
|
|
229
229
|
# xxx) Add the given context to the output
|
230
230
|
ctx = self.context.serialize
|
231
231
|
if result.is_a?(Array)
|
232
|
-
kwgraph = self.context.compact_iri('@graph', vocab: true
|
232
|
+
kwgraph = self.context.compact_iri('@graph', vocab: true)
|
233
233
|
result = result.empty? ? {} : {kwgraph => result}
|
234
234
|
end
|
235
235
|
result = ctx.merge(result) unless ctx.empty?
|
@@ -295,7 +295,7 @@ module JSON::LD
|
|
295
295
|
if context && !flattened.empty?
|
296
296
|
# 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
297
|
compacted = as_array(compact(flattened, ordered: @options[:ordered]))
|
298
|
-
kwgraph = self.context.compact_iri('@graph'
|
298
|
+
kwgraph = self.context.compact_iri('@graph')
|
299
299
|
flattened = self.context.serialize.merge(kwgraph => compacted)
|
300
300
|
end
|
301
301
|
end
|
@@ -313,11 +313,11 @@ module JSON::LD
|
|
313
313
|
# @param [String, #read, Hash, Array] frame
|
314
314
|
# The frame to use when re-arranging the data.
|
315
315
|
# @option options (see #initialize)
|
316
|
-
# @option options ['@always', '@
|
316
|
+
# @option options ['@always', '@link', '@once', '@never'] :embed ('@once')
|
317
317
|
# a flag specifying that objects should be directly embedded in the output, instead of being referred to by their IRI.
|
318
318
|
# @option options [Boolean] :explicit (false)
|
319
319
|
# a flag specifying that for properties to be included in the output, they must be explicitly declared in the framing context.
|
320
|
-
# @option options [Boolean] :requireAll (
|
320
|
+
# @option options [Boolean] :requireAll (false)
|
321
321
|
# 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
322
|
# @option options [Boolean] :omitDefault (false)
|
323
323
|
# a flag specifying that properties that are missing from the JSON-LD input should be omitted from the output.
|
@@ -394,7 +394,7 @@ module JSON::LD
|
|
394
394
|
# Get framing nodes from expanded input, replacing Blank Node identifiers as necessary
|
395
395
|
create_node_map(value, framing_state[:graphMap], active_graph: '@default')
|
396
396
|
|
397
|
-
frame_keys = frame.keys.map {|k| context.expand_iri(k, vocab: true
|
397
|
+
frame_keys = frame.keys.map {|k| context.expand_iri(k, vocab: true)}
|
398
398
|
if frame_keys.include?('@graph')
|
399
399
|
# If frame contains @graph, it matches the default graph.
|
400
400
|
framing_state[:graph] = '@default'
|
@@ -436,7 +436,7 @@ module JSON::LD
|
|
436
436
|
result = if !compacted.is_a?(Array)
|
437
437
|
context.serialize.merge(compacted)
|
438
438
|
else
|
439
|
-
kwgraph = context.compact_iri('@graph'
|
439
|
+
kwgraph = context.compact_iri('@graph')
|
440
440
|
context.serialize.merge({kwgraph => compacted})
|
441
441
|
end
|
442
442
|
log_debug(".frame") {"after compact: #{result.to_json(JSON_STATE) rescue 'malformed json'}"}
|
@@ -535,7 +535,7 @@ module JSON::LD
|
|
535
535
|
# @param [Boolean] extractAllScripts
|
536
536
|
# 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
537
|
# @param [String] profile
|
538
|
-
# When the resulting `contentType` is `text/html`, this option determines the profile to use for selecting a JSON-LD script elements.
|
538
|
+
# 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
539
|
# @param [String] requestProfile
|
540
540
|
# One or more IRIs to use in the request as a profile parameter.
|
541
541
|
# @param [Boolean] validate
|
@@ -612,7 +612,7 @@ module JSON::LD
|
|
612
612
|
# Parse any HTML
|
613
613
|
if remote_doc.document.is_a?(String)
|
614
614
|
remote_doc.document = case remote_doc.contentType
|
615
|
-
when 'text/html'
|
615
|
+
when 'text/html', 'application/xhtml+xml'
|
616
616
|
load_html(remote_doc.document,
|
617
617
|
url: remote_doc.documentUrl,
|
618
618
|
extractAllScripts: extractAllScripts,
|
@@ -628,7 +628,7 @@ module JSON::LD
|
|
628
628
|
|
629
629
|
if remote_doc.contentType && validate
|
630
630
|
raise IOError, "url: #{url}, contentType: #{remote_doc.contentType}" unless
|
631
|
-
remote_doc.contentType.match?(/application\/(.+\+)?json|text\/html/)
|
631
|
+
remote_doc.contentType.match?(/application\/(.+\+)?json|text\/html|application\/xhtml\+xml/)
|
632
632
|
end
|
633
633
|
block_given? ? yield(remote_doc) : remote_doc
|
634
634
|
end
|
@@ -642,7 +642,7 @@ module JSON::LD
|
|
642
642
|
# @param [Boolean] extractAllScripts
|
643
643
|
# 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.
|
644
644
|
# @param [String] profile
|
645
|
-
# When the resulting `contentType` is `text/html`, this option determines the profile to use for selecting a JSON-LD script elements.
|
645
|
+
# 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.
|
646
646
|
# @param [String] requestProfile
|
647
647
|
# One or more IRIs to use in the request as a profile parameter.
|
648
648
|
# @param [Hash<Symbol => Object>] options
|
@@ -739,7 +739,7 @@ module JSON::LD
|
|
739
739
|
elements = if profile
|
740
740
|
es = input.xpath("//script[starts-with(@type, 'application/ld+json;profile=#{profile}')]")
|
741
741
|
# If no profile script, just take a single script without profile
|
742
|
-
es = [input.at_xpath("//script[starts-with(@type, 'application/ld+json')]")] if es.empty?
|
742
|
+
es = [input.at_xpath("//script[starts-with(@type, 'application/ld+json')]")].compact if es.empty?
|
743
743
|
es
|
744
744
|
else
|
745
745
|
input.xpath("//script[starts-with(@type, 'application/ld+json')]")
|
data/lib/json/ld/compact.rb
CHANGED
@@ -5,12 +5,7 @@ module JSON::LD
|
|
5
5
|
include Utils
|
6
6
|
|
7
7
|
# The following constant is used to reduce object allocations in #compact below
|
8
|
-
CONTAINER_MAPPING_ID = %w(@id).freeze
|
9
|
-
CONTAINER_MAPPING_INDEX = %w(@index).freeze
|
10
|
-
CONTAINER_MAPPING_LANGUAGE = %w(@language).freeze
|
11
8
|
CONTAINER_MAPPING_LANGUAGE_INDEX_ID_TYPE = Set.new(%w(@language @index @id @type)).freeze
|
12
|
-
CONTAINER_MAPPING_LIST = %w(@list).freeze
|
13
|
-
CONTAINER_MAPPING_TYPE = %w(@type).freeze
|
14
9
|
EXPANDED_PROPERTY_DIRECTION_INDEX_LANGUAGE_VALUE = %w(@direction @index @language @value).freeze
|
15
10
|
|
16
11
|
##
|
@@ -71,7 +66,7 @@ module JSON::LD
|
|
71
66
|
end
|
72
67
|
|
73
68
|
# If expanded property is @list and we're contained within a list container, recursively compact this item to an array
|
74
|
-
if list?(element) && context.container(property)
|
69
|
+
if list?(element) && context.container(property).include?('@list')
|
75
70
|
return compact(element['@list'], property: property, ordered: ordered)
|
76
71
|
end
|
77
72
|
|
@@ -110,8 +105,7 @@ module JSON::LD
|
|
110
105
|
(context.as_array?(kw_alias) &&
|
111
106
|
!value?(element) &&
|
112
107
|
context.processingMode('json-ld-1.1'))
|
113
|
-
|
114
|
-
result[kw_alias] = compacted_value
|
108
|
+
add_value(result, kw_alias, compacted_value, property_is_array: as_array)
|
115
109
|
next
|
116
110
|
end
|
117
111
|
|
@@ -128,7 +122,7 @@ module JSON::LD
|
|
128
122
|
end
|
129
123
|
|
130
124
|
unless compacted_value.empty?
|
131
|
-
al = context.compact_iri('@reverse'
|
125
|
+
al = context.compact_iri('@reverse')
|
132
126
|
#log_debug("") {"remainder: #{al} => #{compacted_value.inspect}"}
|
133
127
|
result[al] = compacted_value
|
134
128
|
end
|
@@ -146,14 +140,14 @@ module JSON::LD
|
|
146
140
|
next
|
147
141
|
end
|
148
142
|
|
149
|
-
if expanded_property == '@index' && context.container(property)
|
143
|
+
if expanded_property == '@index' && context.container(property).include?('@index')
|
150
144
|
#log_debug("@index") {"drop @index"}
|
151
145
|
next
|
152
146
|
end
|
153
147
|
|
154
148
|
# Otherwise, if expanded property is @direction, @index, @value, or @language:
|
155
149
|
if EXPANDED_PROPERTY_DIRECTION_INDEX_LANGUAGE_VALUE.include?(expanded_property)
|
156
|
-
al = context.compact_iri(expanded_property, vocab: true
|
150
|
+
al = context.compact_iri(expanded_property, vocab: true)
|
157
151
|
#log_debug(expanded_property) {"#{al} => #{expanded_value.inspect}"}
|
158
152
|
result[al] = expanded_value
|
159
153
|
next
|
@@ -209,11 +203,11 @@ module JSON::LD
|
|
209
203
|
# handle @list
|
210
204
|
if list?(expanded_item)
|
211
205
|
compacted_item = as_array(compacted_item)
|
212
|
-
unless container
|
213
|
-
al = context.compact_iri('@list', vocab: true
|
206
|
+
unless container.include?('@list')
|
207
|
+
al = context.compact_iri('@list', vocab: true)
|
214
208
|
compacted_item = {al => compacted_item}
|
215
209
|
if expanded_item.has_key?('@index')
|
216
|
-
key = context.compact_iri('@index', vocab: true
|
210
|
+
key = context.compact_iri('@index', vocab: true)
|
217
211
|
compacted_item[key] = expanded_item['@index']
|
218
212
|
end
|
219
213
|
else
|
@@ -231,11 +225,11 @@ module JSON::LD
|
|
231
225
|
map_object = nest_result[item_active_property] ||= {}
|
232
226
|
# If there is no @id, create a blank node identifier to use as an index
|
233
227
|
map_key = if container.include?('@id') && expanded_item['@id']
|
234
|
-
context.compact_iri(expanded_item['@id']
|
228
|
+
context.compact_iri(expanded_item['@id'])
|
235
229
|
elsif container.include?('@index') && expanded_item['@index']
|
236
|
-
context.compact_iri(expanded_item['@index']
|
230
|
+
context.compact_iri(expanded_item['@index'])
|
237
231
|
else
|
238
|
-
context.compact_iri('@none', vocab: true
|
232
|
+
context.compact_iri('@none', vocab: true)
|
239
233
|
end
|
240
234
|
add_value(map_object, map_key, compacted_item,
|
241
235
|
property_is_array: as_array)
|
@@ -243,7 +237,7 @@ module JSON::LD
|
|
243
237
|
# container includes @graph but not @id or @index and value is a simple graph object
|
244
238
|
if compacted_item.is_a?(Array) && compacted_item.length > 1
|
245
239
|
# Mutple objects in the same graph can't be represented directly, as they would be interpreted as two different graphs. Need to wrap in @included.
|
246
|
-
included_key = context.compact_iri('@included', vocab: true)
|
240
|
+
included_key = context.compact_iri('@included', vocab: true)
|
247
241
|
compacted_item = {included_key => compacted_item}
|
248
242
|
end
|
249
243
|
# Drop through, where compacted_item will be added
|
@@ -251,35 +245,34 @@ module JSON::LD
|
|
251
245
|
property_is_array: as_array)
|
252
246
|
else
|
253
247
|
# container does not include @graph or otherwise does not match one of the previous cases, redo compacted_item
|
254
|
-
al = context.compact_iri('@graph', vocab: true
|
248
|
+
al = context.compact_iri('@graph', vocab: true)
|
255
249
|
compacted_item = {al => compacted_item}
|
256
250
|
if expanded_item['@id']
|
257
|
-
al = context.compact_iri('@id', vocab: true
|
258
|
-
compacted_item[al] = context.compact_iri(expanded_item['@id'], vocab: false
|
251
|
+
al = context.compact_iri('@id', vocab: true)
|
252
|
+
compacted_item[al] = context.compact_iri(expanded_item['@id'], vocab: false)
|
259
253
|
end
|
260
254
|
if expanded_item.has_key?('@index')
|
261
|
-
key = context.compact_iri('@index', vocab: true
|
255
|
+
key = context.compact_iri('@index', vocab: true)
|
262
256
|
compacted_item[key] = expanded_item['@index']
|
263
257
|
end
|
264
258
|
add_value(nest_result, item_active_property, compacted_item,
|
265
259
|
property_is_array: as_array)
|
266
260
|
end
|
267
|
-
elsif container.
|
261
|
+
elsif container.intersect?(CONTAINER_MAPPING_LANGUAGE_INDEX_ID_TYPE) && !container.include?('@graph')
|
268
262
|
map_object = nest_result[item_active_property] ||= {}
|
269
263
|
c = container.first
|
270
|
-
container_key = context.compact_iri(c, vocab: true
|
271
|
-
compacted_item = case
|
272
|
-
when
|
264
|
+
container_key = context.compact_iri(c, vocab: true)
|
265
|
+
compacted_item = case
|
266
|
+
when container.include?('@id')
|
273
267
|
map_key = compacted_item[container_key]
|
274
268
|
compacted_item.delete(container_key)
|
275
269
|
compacted_item
|
276
|
-
when
|
270
|
+
when container.include?('@index')
|
277
271
|
index_key = context.term_definitions[item_active_property].index || '@index'
|
278
272
|
if index_key == '@index'
|
279
273
|
map_key = expanded_item['@index']
|
280
|
-
compacted_item.delete(container_key) if compacted_item.is_a?(Hash)
|
281
274
|
else
|
282
|
-
container_key = context.compact_iri(index_key, vocab: true
|
275
|
+
container_key = context.compact_iri(index_key, vocab: true)
|
283
276
|
map_key, *others = Array(compacted_item[container_key])
|
284
277
|
if map_key.is_a?(String)
|
285
278
|
case others.length
|
@@ -288,15 +281,15 @@ module JSON::LD
|
|
288
281
|
else compacted_item[container_key] = others
|
289
282
|
end
|
290
283
|
else
|
291
|
-
map_key = context.compact_iri('@none', vocab: true
|
284
|
+
map_key = context.compact_iri('@none', vocab: true)
|
292
285
|
end
|
293
286
|
end
|
294
287
|
# Note, if compacted_item is a node reference and key is @id-valued, then this could be compacted further.
|
295
288
|
compacted_item
|
296
|
-
when
|
289
|
+
when container.include?('@language')
|
297
290
|
map_key = expanded_item['@language']
|
298
291
|
value?(expanded_item) ? expanded_item['@value'] : compacted_item
|
299
|
-
when
|
292
|
+
when container.include?('@type')
|
300
293
|
map_key, *types = Array(compacted_item[container_key])
|
301
294
|
case types.length
|
302
295
|
when 0 then compacted_item.delete(container_key)
|
@@ -310,7 +303,7 @@ module JSON::LD
|
|
310
303
|
end
|
311
304
|
compacted_item
|
312
305
|
end
|
313
|
-
map_key ||= context.compact_iri('@none', vocab: true
|
306
|
+
map_key ||= context.compact_iri('@none', vocab: true)
|
314
307
|
add_value(map_object, map_key, compacted_item,
|
315
308
|
property_is_array: as_array)
|
316
309
|
else
|
data/lib/json/ld/context.rb
CHANGED
@@ -3,6 +3,12 @@
|
|
3
3
|
require 'json'
|
4
4
|
require 'bigdecimal'
|
5
5
|
require 'set'
|
6
|
+
begin
|
7
|
+
# Attempt to load this to avoid unnecessary context fetches
|
8
|
+
require 'json-ld-preloaded'
|
9
|
+
rescue LoadError
|
10
|
+
# Silently allow this to fail
|
11
|
+
end
|
6
12
|
|
7
13
|
module JSON::LD
|
8
14
|
class Context
|
@@ -94,7 +100,7 @@ module JSON::LD
|
|
94
100
|
# @param [String] term
|
95
101
|
# @param [String] id
|
96
102
|
# @param [String] type_mapping Type mapping
|
97
|
-
# @param [
|
103
|
+
# @param [Set<'@index', '@language', '@index', '@set', '@type', '@id', '@graph'>] container_mapping
|
98
104
|
# @param [String] language_mapping
|
99
105
|
# Language mapping of term, `false` is used if there is an explicit language mapping for this term
|
100
106
|
# @param ["ltr", "rtl"] direction_mapping
|
@@ -140,12 +146,19 @@ module JSON::LD
|
|
140
146
|
|
141
147
|
# Set container mapping, from an array which may include @set
|
142
148
|
def container_mapping=(mapping)
|
143
|
-
mapping =
|
149
|
+
mapping = case mapping
|
150
|
+
when Set then mapping
|
151
|
+
when Array then Set.new(mapping)
|
152
|
+
when String then Set[mapping]
|
153
|
+
when nil then Set.new
|
154
|
+
else
|
155
|
+
raise "Shouldn't happen with #{mapping.inspect}"
|
156
|
+
end
|
144
157
|
if @as_set = mapping.include?('@set')
|
145
158
|
mapping = mapping.dup
|
146
159
|
mapping.delete('@set')
|
147
160
|
end
|
148
|
-
@container_mapping = mapping
|
161
|
+
@container_mapping = mapping
|
149
162
|
@index ||= '@index' if mapping.include?('@index')
|
150
163
|
end
|
151
164
|
|
@@ -201,7 +214,8 @@ module JSON::LD
|
|
201
214
|
v = instance_variable_get("@#{acc}".to_sym)
|
202
215
|
v = v.to_s if v.is_a?(RDF::Term)
|
203
216
|
if acc == 'container_mapping'
|
204
|
-
v
|
217
|
+
v = v.to_a
|
218
|
+
v << '@set' if as_set?
|
205
219
|
v = v.first if v.length <= 1
|
206
220
|
end
|
207
221
|
defn << "#{acc}: #{v.inspect}" if v
|
@@ -492,7 +506,7 @@ module JSON::LD
|
|
492
506
|
#
|
493
507
|
# @param [Boolean] value
|
494
508
|
def propagate=(value, **options)
|
495
|
-
raise JsonLdError::
|
509
|
+
raise JsonLdError::InvalidContextEntry, "@propagate may only be set in 1.1 mode" if processingMode("json-ld-1.0")
|
496
510
|
raise JsonLdError::InvalidPropagateValue, "@propagate must be boolean valued: #{value.inspect}" unless value.is_a?(TrueClass) || value.is_a?(FalseClass)
|
497
511
|
value
|
498
512
|
end
|
@@ -626,7 +640,7 @@ module JSON::LD
|
|
626
640
|
next unless context.has_key?(key)
|
627
641
|
if key == '@import'
|
628
642
|
# Retrieve remote context and merge the remaining context object into the result.
|
629
|
-
raise JsonLdError::
|
643
|
+
raise JsonLdError::InvalidContextEntry, "@import may only be used in 1.1 mode}" if result.processingMode("json-ld-1.0")
|
630
644
|
raise JsonLdError::InvalidImportValue, "@import must be a string: #{context['@import'].inspect}" unless context['@import'].is_a?(String)
|
631
645
|
source = RDF::URI(result.context_base || result.base).join(context['@import'])
|
632
646
|
begin
|
@@ -640,7 +654,7 @@ module JSON::LD
|
|
640
654
|
raise JsonLdError::InvalidRemoteContext, "#{source}" unless remote_doc.document.is_a?(Hash) && remote_doc.document.has_key?('@context')
|
641
655
|
import_context = remote_doc.document['@context']
|
642
656
|
raise JsonLdError::InvalidRemoteContext, "#{import_context.to_json} must be an object" unless import_context.is_a?(Hash)
|
643
|
-
raise JsonLdError::
|
657
|
+
raise JsonLdError::InvalidContextEntry, "#{import_context.to_json} must not include @import entry" if import_context.has_key?('@import')
|
644
658
|
context.delete(key)
|
645
659
|
context = import_context.merge(context)
|
646
660
|
end
|
@@ -1162,7 +1176,7 @@ module JSON::LD
|
|
1162
1176
|
def container(term)
|
1163
1177
|
return [term] if term == '@list'
|
1164
1178
|
term = find_definition(term)
|
1165
|
-
term ? term.container_mapping :
|
1179
|
+
term ? term.container_mapping : Set.new
|
1166
1180
|
end
|
1167
1181
|
|
1168
1182
|
##
|
@@ -1281,18 +1295,24 @@ module JSON::LD
|
|
1281
1295
|
# Used during Context Processing.
|
1282
1296
|
# @param [Hash] defined
|
1283
1297
|
# Used during Context Processing.
|
1284
|
-
# @param [Boolean]
|
1298
|
+
# @param [Boolean] as_string (false) transform RDF::Resource values to string
|
1285
1299
|
# @param [Hash{Symbol => Object}] options
|
1286
|
-
# @return [RDF::
|
1300
|
+
# @return [RDF::Resource, String]
|
1287
1301
|
# IRI or String, if it's a keyword
|
1288
1302
|
# @raise [JSON::LD::JsonLdError::InvalidIRIMapping] if the value cannot be expanded
|
1289
1303
|
# @see https://www.w3.org/TR/json-ld11-api/#iri-expansion
|
1290
|
-
def expand_iri(value,
|
1291
|
-
|
1304
|
+
def expand_iri(value,
|
1305
|
+
documentRelative: false,
|
1306
|
+
vocab: false,
|
1307
|
+
local_context: nil,
|
1308
|
+
defined: nil,
|
1309
|
+
as_string: false,
|
1310
|
+
**options
|
1311
|
+
)
|
1312
|
+
return (value && as_string ? value.to_s : value) unless value.is_a?(String)
|
1292
1313
|
|
1293
1314
|
return value if KEYWORDS.include?(value)
|
1294
1315
|
return nil if value.match?(/^@[a-zA-Z]+$/)
|
1295
|
-
#log_debug("expand_iri") {"value: #{value.inspect}"} unless quiet
|
1296
1316
|
|
1297
1317
|
defined = defined || {} # if we initialized in the keyword arg we would allocate {} at each invokation, even in the 2 (common) early returns above.
|
1298
1318
|
|
@@ -1302,25 +1322,28 @@ module JSON::LD
|
|
1302
1322
|
end
|
1303
1323
|
|
1304
1324
|
if (v_td = term_definitions[value]) && KEYWORDS.include?(v_td.id)
|
1305
|
-
|
1306
|
-
return v_td.id
|
1325
|
+
return (as_string ? v_td.id.to_s : v_td.id)
|
1307
1326
|
end
|
1308
1327
|
|
1309
1328
|
# If active context has a term definition for value, and the associated mapping is a keyword, return that keyword.
|
1310
1329
|
# If vocab is true and the active context has a term definition for value, return the associated IRI mapping.
|
1311
1330
|
if (v_td = term_definitions[value]) && (vocab || KEYWORDS.include?(v_td.id))
|
1312
|
-
|
1313
|
-
return v_td.id
|
1331
|
+
return (as_string ? v_td.id.to_s : v_td.id)
|
1314
1332
|
end
|
1315
1333
|
|
1316
1334
|
# If value contains a colon (:), it is either an absolute IRI or a compact IRI:
|
1317
1335
|
if value[1..-1].to_s.include?(':')
|
1318
1336
|
prefix, suffix = value.split(':', 2)
|
1319
|
-
#log_debug("") {"prefix: #{prefix.inspect}, suffix: #{suffix.inspect}, vocab: #{self.vocab.inspect}"} unless quiet
|
1320
1337
|
|
1321
1338
|
# If prefix is underscore (_) or suffix begins with double-forward-slash (//), return value as it is already an absolute IRI or a blank node identifier.
|
1322
|
-
|
1323
|
-
|
1339
|
+
if prefix == '_'
|
1340
|
+
v = RDF::Node.new(namer.get_sym(suffix))
|
1341
|
+
return (as_string ? v.to_s : v)
|
1342
|
+
end
|
1343
|
+
if suffix.start_with?('//')
|
1344
|
+
v = RDF::URI(value)
|
1345
|
+
return (as_string ? v.to_s : v)
|
1346
|
+
end
|
1324
1347
|
|
1325
1348
|
# If local context is not null, it contains a key that equals prefix, and the value associated with the key that equals prefix in defined is not true, invoke the Create Term Definition algorithm, passing active context, local context, prefix as term, and defined. This will ensure that a term definition is created for prefix in active context during Context Processing.
|
1326
1349
|
if local_context && local_context.has_key?(prefix) && !defined[prefix]
|
@@ -1329,15 +1352,14 @@ module JSON::LD
|
|
1329
1352
|
|
1330
1353
|
# If active context contains a term definition for prefix, return the result of concatenating the IRI mapping associated with prefix and suffix.
|
1331
1354
|
if (td = term_definitions[prefix]) && !td.id.nil? && td.prefix?
|
1332
|
-
return td.id + suffix
|
1355
|
+
return (as_string ? td.id.to_s : td.id) + suffix
|
1333
1356
|
elsif RDF::URI(value).absolute?
|
1334
1357
|
# Otherwise, if the value has the form of an absolute IRI, return it
|
1335
|
-
return RDF::URI(value)
|
1358
|
+
return (as_string ? value.to_s : RDF::URI(value))
|
1336
1359
|
else
|
1337
1360
|
# Otherwise, it is a relative IRI
|
1338
1361
|
end
|
1339
1362
|
end
|
1340
|
-
#log_debug("") {"=> #{result.inspect}"} unless quiet
|
1341
1363
|
|
1342
1364
|
result = if vocab && self.vocab
|
1343
1365
|
# If vocab is true, and active context has a vocabulary mapping, return the result of concatenating the vocabulary mapping with value.
|
@@ -1352,8 +1374,7 @@ module JSON::LD
|
|
1352
1374
|
else
|
1353
1375
|
RDF::URI(value)
|
1354
1376
|
end
|
1355
|
-
|
1356
|
-
result
|
1377
|
+
result && as_string ? result.to_s : result
|
1357
1378
|
end
|
1358
1379
|
|
1359
1380
|
# The following constants are used to reduce object allocations in #compact_iri below
|
@@ -1378,18 +1399,15 @@ module JSON::LD
|
|
1378
1399
|
# specifies whether the passed iri should be compacted using the active context's vocabulary mapping
|
1379
1400
|
# @param [Boolean] reverse
|
1380
1401
|
# specifies whether a reverse property is being compacted
|
1381
|
-
# @param [Boolean] quiet (false)
|
1382
1402
|
# @param [Hash{Symbol => Object}] options ({})
|
1383
1403
|
#
|
1384
1404
|
# @return [String] compacted form of IRI
|
1385
1405
|
# @see https://www.w3.org/TR/json-ld11-api/#iri-compaction
|
1386
|
-
def compact_iri(iri, value: nil, vocab: nil, reverse: false,
|
1406
|
+
def compact_iri(iri, value: nil, vocab: nil, reverse: false, **options)
|
1387
1407
|
return if iri.nil?
|
1388
1408
|
iri = iri.to_s
|
1389
|
-
#log_debug("compact_iri(#{iri.inspect}", options) {[value, vocab, reverse].inspect} unless quiet
|
1390
1409
|
|
1391
1410
|
if vocab && inverse_context.has_key?(iri)
|
1392
|
-
#log_debug("") {"vocab and key in inverse context"} unless quiet
|
1393
1411
|
default_language = if self.default_direction
|
1394
1412
|
"#{self.default_language}_#{self.default_direction}".downcase
|
1395
1413
|
else
|
@@ -1406,7 +1424,6 @@ module JSON::LD
|
|
1406
1424
|
tl, tl_value = "@type", "@reverse"
|
1407
1425
|
containers << '@set'
|
1408
1426
|
elsif list?(value)
|
1409
|
-
#log_debug("") {"list(#{value.inspect})"} unless quiet
|
1410
1427
|
# if value is a list object, then set type/language and type/language value to the most specific values that work for all items in the list as follows:
|
1411
1428
|
containers << "@list" unless index?(value)
|
1412
1429
|
list = value['@list']
|
@@ -1429,25 +1446,21 @@ module JSON::LD
|
|
1429
1446
|
end
|
1430
1447
|
common_language ||= item_language
|
1431
1448
|
if item_language != common_language && value?(item)
|
1432
|
-
#log_debug("") {"-- #{item_language} conflicts with #{common_language}, use @none"} unless quiet
|
1433
1449
|
common_language = '@none'
|
1434
1450
|
end
|
1435
1451
|
common_type ||= item_type
|
1436
1452
|
if item_type != common_type
|
1437
1453
|
common_type = '@none'
|
1438
|
-
#log_debug("") {"#{item_type} conflicts with #{common_type}, use @none"} unless quiet
|
1439
1454
|
end
|
1440
1455
|
end
|
1441
1456
|
|
1442
1457
|
common_language ||= '@none'
|
1443
1458
|
common_type ||= '@none'
|
1444
|
-
#log_debug("") {"common type: #{common_type}, common language: #{common_language}"} unless quiet
|
1445
1459
|
if common_type != '@none'
|
1446
1460
|
tl, tl_value = '@type', common_type
|
1447
1461
|
else
|
1448
1462
|
tl_value = common_language
|
1449
1463
|
end
|
1450
|
-
#log_debug("") {"list: containers: #{containers.inspect}, type/language: #{tl.inspect}, type/language value: #{tl_value.inspect}"} unless quiet
|
1451
1464
|
elsif graph?(value)
|
1452
1465
|
# Prefer @index and @id containers, then @graph, then @index
|
1453
1466
|
containers.concat(CONTAINERS_GRAPH_INDEX_INDEX) if index?(value)
|
@@ -1482,7 +1495,6 @@ module JSON::LD
|
|
1482
1495
|
tl, tl_value = '@type', '@id'
|
1483
1496
|
end
|
1484
1497
|
containers << '@set'
|
1485
|
-
#log_debug("") {"value: containers: #{containers.inspect}, type/language: #{tl.inspect}, type/language value: #{tl_value.inspect}"} unless quiet
|
1486
1498
|
end
|
1487
1499
|
|
1488
1500
|
containers << '@none'
|
@@ -1506,7 +1518,6 @@ module JSON::LD
|
|
1506
1518
|
tl = '@any' if list?(value) && value['@list'].empty?
|
1507
1519
|
preferred_values.concat([tl_value, '@none'].compact)
|
1508
1520
|
end
|
1509
|
-
#log_debug("") {"preferred_values: #{preferred_values.inspect}"} unless quiet
|
1510
1521
|
preferred_values << '@any'
|
1511
1522
|
|
1512
1523
|
# if containers included `@language` and preferred_values includes something of the form language-tag_direction, add just the _direction part, to select terms that have that direction.
|
@@ -1515,7 +1526,6 @@ module JSON::LD
|
|
1515
1526
|
end
|
1516
1527
|
|
1517
1528
|
if p_term = select_term(iri, containers, tl, preferred_values)
|
1518
|
-
#log_debug("") {"=> term: #{p_term.inspect}"} unless quiet
|
1519
1529
|
return p_term
|
1520
1530
|
end
|
1521
1531
|
end
|
@@ -1523,7 +1533,6 @@ module JSON::LD
|
|
1523
1533
|
# At this point, there is no simple term that iri can be compacted to. If vocab is true and active context has a vocabulary mapping:
|
1524
1534
|
if vocab && self.vocab && iri.start_with?(self.vocab) && iri.length > self.vocab.length
|
1525
1535
|
suffix = iri[self.vocab.length..-1]
|
1526
|
-
#log_debug("") {"=> vocab suffix: #{suffix.inspect}"} unless quiet
|
1527
1536
|
return suffix unless term_definitions.has_key?(suffix)
|
1528
1537
|
end
|
1529
1538
|
|
@@ -1566,10 +1575,8 @@ module JSON::LD
|
|
1566
1575
|
if !vocab
|
1567
1576
|
# transform iri to a relative IRI using the document's base IRI
|
1568
1577
|
iri = remove_base(iri)
|
1569
|
-
#log_debug("") {"=> relative iri: #{iri.inspect}"} unless quiet
|
1570
1578
|
return iri
|
1571
1579
|
else
|
1572
|
-
#log_debug("") {"=> absolute iri: #{iri.inspect}"} unless quiet
|
1573
1580
|
return iri
|
1574
1581
|
end
|
1575
1582
|
end
|
@@ -1830,9 +1837,9 @@ module JSON::LD
|
|
1830
1837
|
|
1831
1838
|
private
|
1832
1839
|
|
1833
|
-
CONTEXT_CONTAINER_ARRAY_TERMS = %w(@set @list @graph).freeze
|
1834
|
-
CONTEXT_CONTAINER_ID_GRAPH = %w(@id @graph).freeze
|
1835
|
-
CONTEXT_CONTAINER_INDEX_GRAPH = %w(@index @graph).freeze
|
1840
|
+
CONTEXT_CONTAINER_ARRAY_TERMS = Set.new(%w(@set @list @graph)).freeze
|
1841
|
+
CONTEXT_CONTAINER_ID_GRAPH = Set.new(%w(@id @graph)).freeze
|
1842
|
+
CONTEXT_CONTAINER_INDEX_GRAPH = Set.new(%w(@index @graph)).freeze
|
1836
1843
|
CONTEXT_BASE_FRAG_OR_QUERY = %w(? #).freeze
|
1837
1844
|
CONTEXT_TYPE_ID_VOCAB = %w(@id @vocab).freeze
|
1838
1845
|
|
@@ -1906,7 +1913,7 @@ module JSON::LD
|
|
1906
1913
|
end.each do |term|
|
1907
1914
|
next unless td = term_definitions[term]
|
1908
1915
|
|
1909
|
-
container = td.container_mapping.join('')
|
1916
|
+
container = td.container_mapping.to_a.join('')
|
1910
1917
|
if container.empty?
|
1911
1918
|
container = td.as_set? ? %(@set) : %(@none)
|
1912
1919
|
end
|
@@ -1943,7 +1950,7 @@ module JSON::LD
|
|
1943
1950
|
lang_dir = td.direction_mapping ? "_#{td.direction_mapping}" : '@none'
|
1944
1951
|
language_map[lang_dir] ||= term
|
1945
1952
|
elsif default_direction
|
1946
|
-
language_map[
|
1953
|
+
language_map["_#{default_direction}"] ||= term
|
1947
1954
|
language_map['@none'] ||= term
|
1948
1955
|
type_map['@none'] ||= term
|
1949
1956
|
else
|
@@ -2058,7 +2065,7 @@ module JSON::LD
|
|
2058
2065
|
"'@container' on term #{term.inspect} must be a string: #{container.inspect}"
|
2059
2066
|
end
|
2060
2067
|
|
2061
|
-
val = Array(container)
|
2068
|
+
val = Set.new(Array(container))
|
2062
2069
|
val.delete('@set') if has_set = val.include?('@set')
|
2063
2070
|
|
2064
2071
|
if val.include?('@list')
|
@@ -2088,7 +2095,7 @@ module JSON::LD
|
|
2088
2095
|
processingMode('json-ld-1.0')
|
2089
2096
|
raise JsonLdError::InvalidContainerMapping,
|
2090
2097
|
"'@container' on term #{term.inspect} using @id cannot have any values other than @set and/or @graph, found #{container.inspect}" unless
|
2091
|
-
(
|
2098
|
+
val.subset?(CONTEXT_CONTAINER_ID_GRAPH)
|
2092
2099
|
# Okay
|
2093
2100
|
elsif val.include?('@type') || val.include?('@graph')
|
2094
2101
|
raise JsonLdError::InvalidContainerMapping,
|
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
|
@@ -36,7 +31,7 @@ module JSON::LD
|
|
36
31
|
def expand(input, active_property, context, ordered: false, framing: false, from_map: false)
|
37
32
|
#log_debug("expand") {"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) 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]
|
@@ -44,7 +39,7 @@ module JSON::LD
|
|
44
39
|
result = case input
|
45
40
|
when Array
|
46
41
|
# If element is an array,
|
47
|
-
is_list = context.container(active_property)
|
42
|
+
is_list = context.container(active_property).include?('@list')
|
48
43
|
value = input.each_with_object([]) do |v, memo|
|
49
44
|
# Initialize expanded item to the result of using this algorithm recursively, passing active context, active property, and item as element.
|
50
45
|
v = expand(v, active_property, context, ordered: ordered, framing: framing, from_map: from_map)
|
@@ -62,7 +57,7 @@ module JSON::LD
|
|
62
57
|
value
|
63
58
|
when Hash
|
64
59
|
if context.previous_context
|
65
|
-
expanded_key_map = input.keys.inject({}) {|memo, key| memo.merge(key => context.expand_iri(key, vocab: true)
|
60
|
+
expanded_key_map = input.keys.inject({}) {|memo, key| memo.merge(key => context.expand_iri(key, vocab: true, as_string: true))}
|
66
61
|
# Revert any previously type-scoped term definitions, unless this is from a map, a value object or a subject reference
|
67
62
|
revert_context = !from_map &&
|
68
63
|
!expanded_key_map.values.include?('@value') &&
|
@@ -139,7 +134,7 @@ module JSON::LD
|
|
139
134
|
raise JsonLdError::InvalidLanguageTaggedValue,
|
140
135
|
"when @language is used, @value must be a string: #{output_object.inspect}"
|
141
136
|
elsif !Array(output_object['@type']).all? {|t|
|
142
|
-
t.is_a?(String) && RDF::URI(t).
|
137
|
+
t.is_a?(String) && RDF::URI(t).valid? && !t.start_with?('_:') ||
|
143
138
|
t.is_a?(Hash) && t.empty?}
|
144
139
|
# 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
140
|
raise JsonLdError::InvalidTypedValue,
|
@@ -164,8 +159,8 @@ module JSON::LD
|
|
164
159
|
|
165
160
|
# If active property is null or @graph, drop free-floating values as follows:
|
166
161
|
if (expanded_active_property || '@graph') == '@graph' &&
|
167
|
-
|
168
|
-
(output_object.keys -
|
162
|
+
(output_object.key?('@value') || output_object.key?('@list') ||
|
163
|
+
(output_object.keys - KEY_ID).empty? && !framing)
|
169
164
|
#log_debug(" =>") { "empty top-level: " + output_object.inspect}
|
170
165
|
return nil
|
171
166
|
end
|
@@ -202,14 +197,14 @@ module JSON::LD
|
|
202
197
|
nests = []
|
203
198
|
|
204
199
|
input_type = Array(input[type_key]).last
|
205
|
-
input_type = context.expand_iri(input_type, vocab: true,
|
200
|
+
input_type = context.expand_iri(input_type, vocab: true, as_string: true) if input_type
|
206
201
|
|
207
202
|
# Then, proceed and process each property and value in element as follows:
|
208
203
|
keys = ordered ? input.keys.sort : input.keys
|
209
204
|
keys.each do |key|
|
210
205
|
# For each key and value in element, ordered lexicographically by key:
|
211
206
|
value = input[key]
|
212
|
-
expanded_property = context.expand_iri(key, vocab: true
|
207
|
+
expanded_property = context.expand_iri(key, vocab: true)
|
213
208
|
|
214
209
|
# 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
210
|
next if expanded_property.is_a?(RDF::URI) && expanded_property.relative?
|
@@ -241,16 +236,16 @@ module JSON::LD
|
|
241
236
|
# If expanded property is @id and value is not a string, an invalid @id value error has been detected and processing is aborted
|
242
237
|
e_id = case value
|
243
238
|
when String
|
244
|
-
context.expand_iri(value, documentRelative: true,
|
239
|
+
context.expand_iri(value, documentRelative: true, as_string: true)
|
245
240
|
when Array
|
246
241
|
# Framing allows an array of IRIs, and always puts values in an array
|
247
242
|
raise JsonLdError::InvalidIdValue,
|
248
243
|
"value of @id must be a string unless framing: #{value.inspect}" unless framing
|
249
|
-
context.expand_iri(value, documentRelative: true,
|
244
|
+
context.expand_iri(value, documentRelative: true, as_string: true)
|
250
245
|
value.map do |v|
|
251
246
|
raise JsonLdError::InvalidTypeValue,
|
252
247
|
"@id value must be a string or array of strings for framing: #{v.inspect}" unless v.is_a?(String)
|
253
|
-
context.expand_iri(v, documentRelative: true,
|
248
|
+
context.expand_iri(v, documentRelative: true, as_string: true)
|
254
249
|
end
|
255
250
|
when Hash
|
256
251
|
raise JsonLdError::InvalidIdValue,
|
@@ -287,21 +282,21 @@ module JSON::LD
|
|
287
282
|
value.map do |v|
|
288
283
|
raise JsonLdError::InvalidTypeValue,
|
289
284
|
"@type value must be a string or array of strings: #{v.inspect}" unless v.is_a?(String)
|
290
|
-
type_scoped_context.expand_iri(v, vocab: true, documentRelative: true,
|
285
|
+
type_scoped_context.expand_iri(v, vocab: true, documentRelative: true, as_string: true)
|
291
286
|
end
|
292
287
|
when String
|
293
|
-
type_scoped_context.expand_iri(value, vocab: true, documentRelative: true,
|
288
|
+
type_scoped_context.expand_iri(value, vocab: true, documentRelative: true, as_string: true)
|
294
289
|
when Hash
|
295
290
|
if !framing
|
296
291
|
raise JsonLdError::InvalidTypeValue,
|
297
292
|
"@type value must be a string or array of strings: #{value.inspect}"
|
298
293
|
elsif value.keys.length == 1 &&
|
299
|
-
type_scoped_context.expand_iri(value.keys.first, vocab: true
|
294
|
+
type_scoped_context.expand_iri(value.keys.first, vocab: true) == '@default'
|
300
295
|
# Expand values of @default, which must be a string, or array of strings expanding to IRIs
|
301
296
|
[{'@default' => Array(value['@default']).map do |v|
|
302
297
|
raise JsonLdError::InvalidTypeValue,
|
303
298
|
"@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, vocab: true, documentRelative: true,
|
299
|
+
type_scoped_context.expand_iri(v, vocab: true, documentRelative: true, as_string: true)
|
305
300
|
end}]
|
306
301
|
elsif !value.empty?
|
307
302
|
raise JsonLdError::InvalidTypeValue,
|
@@ -481,7 +476,7 @@ module JSON::LD
|
|
481
476
|
expanded_value = if context.coerce(key) == '@json'
|
482
477
|
# In JSON-LD 1.1, values can be native JSON
|
483
478
|
{"@value" => value, "@type" => "@json"}
|
484
|
-
elsif container.
|
479
|
+
elsif container.include?('@language') && value.is_a?(Hash)
|
485
480
|
# 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
481
|
|
487
482
|
# Set multilingual array to an empty array.
|
@@ -490,7 +485,7 @@ module JSON::LD
|
|
490
485
|
# For each key-value pair language-language value in value, ordered lexicographically by language
|
491
486
|
keys = ordered ? value.keys.sort : value.keys
|
492
487
|
keys.each do |k|
|
493
|
-
expanded_k = context.expand_iri(k, vocab: true,
|
488
|
+
expanded_k = context.expand_iri(k, vocab: true, as_string: true)
|
494
489
|
|
495
490
|
if k !~ /^[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*$/ && expanded_k != '@none'
|
496
491
|
warn "@language must be valid BCP47: #{k.inspect}"
|
@@ -510,7 +505,7 @@ module JSON::LD
|
|
510
505
|
end
|
511
506
|
|
512
507
|
ary
|
513
|
-
elsif container.
|
508
|
+
elsif container.intersect?(CONTAINER_INDEX_ID_TYPE) && value.is_a?(Hash)
|
514
509
|
# 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
510
|
|
516
511
|
# Set ary to an empty array.
|
@@ -535,13 +530,13 @@ module JSON::LD
|
|
535
530
|
map_context = container_context.parse(map_context, propagate: false) if map_context
|
536
531
|
map_context ||= container_context
|
537
532
|
|
538
|
-
expanded_k = container_context.expand_iri(k, vocab: true,
|
533
|
+
expanded_k = container_context.expand_iri(k, vocab: true, as_string: true)
|
539
534
|
|
540
535
|
# Initialize index value to the result of using this algorithm recursively, passing active context, key as active property, and index value as element.
|
541
536
|
index_value = expand([value[k]].flatten, key, map_context, ordered: ordered, framing: framing, from_map: true)
|
542
537
|
index_value.each do |item|
|
543
|
-
case
|
544
|
-
when
|
538
|
+
case
|
539
|
+
when container.include?('@index')
|
545
540
|
# Indexed graph by graph name
|
546
541
|
if !graph?(item) && container.include?('@graph')
|
547
542
|
item = {'@graph' => as_array(item)}
|
@@ -553,18 +548,18 @@ module JSON::LD
|
|
553
548
|
else
|
554
549
|
# Expand key based on term
|
555
550
|
expanded_k = k == '@none' ? '@none' : container_context.expand_value(index_key, k)
|
556
|
-
index_property = container_context.expand_iri(index_key, vocab: true,
|
551
|
+
index_property = container_context.expand_iri(index_key, vocab: true, as_string: true)
|
557
552
|
item[index_property] = [expanded_k].concat(Array(item[index_property])) unless expanded_k == '@none'
|
558
553
|
end
|
559
|
-
when
|
554
|
+
when container.include?('@id')
|
560
555
|
# Indexed graph by graph name
|
561
556
|
if !graph?(item) && container.include?('@graph')
|
562
557
|
item = {'@graph' => as_array(item)}
|
563
558
|
end
|
564
559
|
# Expand k document relative
|
565
|
-
expanded_k = container_context.expand_iri(k, documentRelative: true,
|
560
|
+
expanded_k = container_context.expand_iri(k, documentRelative: true, as_string: true) unless expanded_k == '@none'
|
566
561
|
item['@id'] ||= expanded_k unless expanded_k == '@none'
|
567
|
-
when
|
562
|
+
when container.include?('@type')
|
568
563
|
item['@type'] = [expanded_k].concat(Array(item['@type'])) unless expanded_k == '@none'
|
569
564
|
end
|
570
565
|
|
data/lib/json/ld/frame.rb
CHANGED
@@ -119,7 +119,7 @@ module JSON::LD
|
|
119
119
|
end
|
120
120
|
end
|
121
121
|
|
122
|
-
# If frame has `@included`, recurse over
|
122
|
+
# If frame has `@included`, recurse over its sub-frame
|
123
123
|
if frame['@included']
|
124
124
|
frame(state.merge(embedded: false), subjects, frame['@included'], parent: output, property: '@included', **options)
|
125
125
|
end
|
data/lib/json/ld/from_rdf.rb
CHANGED
@@ -42,7 +42,7 @@ module JSON::LD
|
|
42
42
|
|
43
43
|
default_graph[name] ||= {'@id' => name} unless name == '@default'
|
44
44
|
|
45
|
-
subject = ec.expand_iri(statement.subject)
|
45
|
+
subject = ec.expand_iri(statement.subject, as_string: true)
|
46
46
|
node = node_map[subject] ||= {'@id' => subject}
|
47
47
|
|
48
48
|
# If predicate is rdf:datatype, note subject in compound literal subjects map
|
data/lib/json/ld/to_rdf.rb
CHANGED
@@ -50,11 +50,11 @@ module JSON::LD
|
|
50
50
|
# Either serialize using a datatype, or a compound-literal
|
51
51
|
case @options[:rdfDirection]
|
52
52
|
when 'i18n-datatype'
|
53
|
-
datatype = RDF::URI("https://www.w3.org/ns/i18n##{item.fetch('@language', '')}_#{item['@direction']}")
|
53
|
+
datatype = RDF::URI("https://www.w3.org/ns/i18n##{item.fetch('@language', '').downcase}_#{item['@direction']}")
|
54
54
|
when 'compound-literal'
|
55
55
|
cl = RDF::Node.new
|
56
56
|
yield RDF::Statement(cl, RDF.value, item['@value'].to_s)
|
57
|
-
yield RDF::Statement(cl, RDF.to_uri + 'language', item['@language']) if item['@language']
|
57
|
+
yield RDF::Statement(cl, RDF.to_uri + 'language', item['@language'].downcase) if item['@language']
|
58
58
|
yield RDF::Statement(cl, RDF.to_uri + 'direction', item['@direction'])
|
59
59
|
return cl
|
60
60
|
end
|
@@ -76,7 +76,13 @@ module JSON::LD
|
|
76
76
|
return parse_list(item['@list'], graph_name: graph_name, &block)
|
77
77
|
end
|
78
78
|
|
79
|
-
|
79
|
+
# Skip if '@id' is nil
|
80
|
+
subject = if item.has_key?('@id')
|
81
|
+
item['@id'].nil? ? nil : as_resource(item['@id'])
|
82
|
+
else
|
83
|
+
node
|
84
|
+
end
|
85
|
+
|
80
86
|
#log_debug("item_to_rdf") {"subject: #{subject.to_ntriples rescue 'malformed rdf'}"}
|
81
87
|
item.each do |property, values|
|
82
88
|
case property
|
data/lib/json/ld/writer.rb
CHANGED
@@ -143,8 +143,8 @@ module JSON::LD
|
|
143
143
|
datatype: TrueClass,
|
144
144
|
default: true,
|
145
145
|
control: :checkbox,
|
146
|
-
on: ["--[no-]
|
147
|
-
description: "Require all properties to match (true). Default is `true` use --no-
|
146
|
+
on: ["--[no-]require-all"],
|
147
|
+
description: "Require all properties to match (true). Default is `true` use --no-require-all to disable.") {|arg| arg},
|
148
148
|
RDF::CLI::Option.new(
|
149
149
|
symbol: :stream,
|
150
150
|
datatype: TrueClass,
|
data/spec/context_spec.rb
CHANGED
@@ -348,7 +348,7 @@ describe JSON::LD::Context do
|
|
348
348
|
expect(subject.parse({
|
349
349
|
"foo" => {"@id" => "http://example.com/", "@container" => "@list"}
|
350
350
|
}).containers).to produce({
|
351
|
-
"foo" =>
|
351
|
+
"foo" => Set["@list"]
|
352
352
|
}, logger)
|
353
353
|
end
|
354
354
|
|
@@ -356,7 +356,7 @@ describe JSON::LD::Context do
|
|
356
356
|
expect(subject.parse({
|
357
357
|
"foo" => {"@id" => "http://example.com/", "@container" => "@type"}
|
358
358
|
}).containers).to produce({
|
359
|
-
"foo" =>
|
359
|
+
"foo" => Set["@type"]
|
360
360
|
}, logger)
|
361
361
|
end
|
362
362
|
|
@@ -364,7 +364,7 @@ describe JSON::LD::Context do
|
|
364
364
|
expect(subject.parse({
|
365
365
|
"foo" => {"@id" => "http://example.com/", "@container" => "@id"}
|
366
366
|
}).containers).to produce({
|
367
|
-
"foo" =>
|
367
|
+
"foo" => Set["@id"]
|
368
368
|
}, logger)
|
369
369
|
end
|
370
370
|
|
@@ -553,12 +553,12 @@ describe JSON::LD::Context do
|
|
553
553
|
end
|
554
554
|
end
|
555
555
|
|
556
|
-
it "generates
|
557
|
-
expect {context.parse({'@propagate' => true})}.to raise_error(JSON::LD::JsonLdError::
|
556
|
+
it "generates InvalidContextEntry if using @propagate" do
|
557
|
+
expect {context.parse({'@propagate' => true})}.to raise_error(JSON::LD::JsonLdError::InvalidContextEntry)
|
558
558
|
end
|
559
559
|
|
560
|
-
it "generates
|
561
|
-
expect {context.parse({'@import' => "location"})}.to raise_error(JSON::LD::JsonLdError::
|
560
|
+
it "generates InvalidContextEntry if using @import" do
|
561
|
+
expect {context.parse({'@import' => "location"})}.to raise_error(JSON::LD::JsonLdError::InvalidContextEntry)
|
562
562
|
end
|
563
563
|
|
564
564
|
(JSON::LD::KEYWORDS - %w(@base @language @version @protected @propagate @vocab)).each do |kw|
|
@@ -1796,23 +1796,23 @@ describe JSON::LD::Context do
|
|
1796
1796
|
|
1797
1797
|
it "uses TermDefinition" do
|
1798
1798
|
{
|
1799
|
-
"ex" =>
|
1800
|
-
"graph" =>
|
1801
|
-
"graphSet" =>
|
1802
|
-
"graphId" =>
|
1803
|
-
"graphIdSet" =>
|
1804
|
-
"graphNdx" =>
|
1805
|
-
"graphNdxSet" =>
|
1806
|
-
"id" =>
|
1807
|
-
"idSet" =>
|
1808
|
-
"language" =>
|
1809
|
-
"langSet" =>
|
1810
|
-
"list" =>
|
1811
|
-
"ndx" =>
|
1812
|
-
"ndxSet" =>
|
1813
|
-
"set" =>
|
1814
|
-
"type" =>
|
1815
|
-
"typeSet" =>
|
1799
|
+
"ex" => Set.new,
|
1800
|
+
"graph" => Set["@graph"],
|
1801
|
+
"graphSet" => Set["@graph"],
|
1802
|
+
"graphId" => Set["@graph", "@id"],
|
1803
|
+
"graphIdSet" => Set["@graph", "@id"],
|
1804
|
+
"graphNdx" => Set["@graph", "@index"],
|
1805
|
+
"graphNdxSet" => Set["@graph", "@index"],
|
1806
|
+
"id" => Set['@id'],
|
1807
|
+
"idSet" => Set['@id'],
|
1808
|
+
"language" => Set['@language'],
|
1809
|
+
"langSet" => Set['@language'],
|
1810
|
+
"list" => Set['@list'],
|
1811
|
+
"ndx" => Set['@index'],
|
1812
|
+
"ndxSet" => Set['@index'],
|
1813
|
+
"set" => Set.new,
|
1814
|
+
"type" => Set['@type'],
|
1815
|
+
"typeSet" => Set['@type'],
|
1816
1816
|
}.each do |defn, container|
|
1817
1817
|
expect(subject.container(subject.term_definitions[defn])).to eq container
|
1818
1818
|
end
|
@@ -1844,23 +1844,23 @@ describe JSON::LD::Context do
|
|
1844
1844
|
|
1845
1845
|
it "uses array" do
|
1846
1846
|
{
|
1847
|
-
"ex" =>
|
1848
|
-
"graph" =>
|
1849
|
-
"graphSet" =>
|
1850
|
-
"graphId" =>
|
1851
|
-
"graphIdSet" =>
|
1852
|
-
"graphNdx" =>
|
1853
|
-
"graphNdxSet" =>
|
1854
|
-
"id" =>
|
1855
|
-
"idSet" =>
|
1856
|
-
"language" =>
|
1857
|
-
"langSet" =>
|
1858
|
-
"list" =>
|
1859
|
-
"ndx" =>
|
1860
|
-
"ndxSet" =>
|
1861
|
-
"set" =>
|
1862
|
-
"type" =>
|
1863
|
-
"typeSet" =>
|
1847
|
+
"ex" => Set.new,
|
1848
|
+
"graph" => Set["@graph"],
|
1849
|
+
"graphSet" => Set["@graph"],
|
1850
|
+
"graphId" => Set["@graph", "@id"],
|
1851
|
+
"graphIdSet" => Set["@graph", "@id"],
|
1852
|
+
"graphNdx" => Set["@graph", "@index"],
|
1853
|
+
"graphNdxSet" => Set["@graph", "@index"],
|
1854
|
+
"id" => Set['@id'],
|
1855
|
+
"idSet" => Set['@id'],
|
1856
|
+
"language" => Set['@language'],
|
1857
|
+
"langSet" => Set['@language'],
|
1858
|
+
"list" => Set['@list'],
|
1859
|
+
"ndx" => Set['@index'],
|
1860
|
+
"ndxSet" => Set['@index'],
|
1861
|
+
"set" => Set.new,
|
1862
|
+
"type" => Set['@type'],
|
1863
|
+
"typeSet" => Set['@type'],
|
1864
1864
|
}.each do |defn, container|
|
1865
1865
|
expect(subject.container(defn)).to eq container
|
1866
1866
|
end
|
@@ -2053,13 +2053,13 @@ describe JSON::LD::Context do
|
|
2053
2053
|
|
2054
2054
|
context "with container_mapping @id @set" do
|
2055
2055
|
subject {described_class.new("term", container_mapping: %w(@id @set))}
|
2056
|
-
its(:container_mapping) {is_expected.to eq
|
2056
|
+
its(:container_mapping) {is_expected.to eq Set['@id']}
|
2057
2057
|
its(:to_rb) {is_expected.to eq %(TermDefinition.new("term", container_mapping: ["@id", "@set"]))}
|
2058
2058
|
end
|
2059
2059
|
|
2060
2060
|
context "with container_mapping @list" do
|
2061
2061
|
subject {described_class.new("term", container_mapping: "@list")}
|
2062
|
-
its(:container_mapping) {is_expected.to eq
|
2062
|
+
its(:container_mapping) {is_expected.to eq Set['@list']}
|
2063
2063
|
its(:to_rb) {is_expected.to eq %(TermDefinition.new("term", container_mapping: "@list"))}
|
2064
2064
|
end
|
2065
2065
|
|
data/spec/suite_expand_spec.rb
CHANGED
@@ -7,6 +7,8 @@ describe JSON::LD do
|
|
7
7
|
m = Fixtures::SuiteTest::Manifest.open("#{Fixtures::SuiteTest::SUITE}expand-manifest.jsonld")
|
8
8
|
describe m.name do
|
9
9
|
m.entries.each do |t|
|
10
|
+
# MultiJson use OJ, by default, which doesn't handle native numbers the same as the JSON gem.
|
11
|
+
t.options[:adapter] = :json_gem if %w(#tjs12).include?(t.property('@id'))
|
10
12
|
specify "#{t.property('@id')}: #{t.name} unordered#{' (negative test)' unless t.positiveTest?}" do
|
11
13
|
t.options[:ordered] = false
|
12
14
|
if %w(#t0068).include?(t.property('@id'))
|
data/spec/suite_helper.rb
CHANGED
@@ -132,6 +132,10 @@ module Fixtures
|
|
132
132
|
{'specVersion' => "json-ld-1.1"}.merge(property('option') || {}).each do |k, v|
|
133
133
|
opts[k.to_sym] = v
|
134
134
|
end
|
135
|
+
if opts[:expandContext] && !RDF::URI(opts[:expandContext]).absolute?
|
136
|
+
# Resolve relative to manifest location
|
137
|
+
opts[:expandContext] = manifest_url.join(opts[:expandContext]).to_s
|
138
|
+
end
|
135
139
|
opts
|
136
140
|
end
|
137
141
|
end
|
@@ -144,6 +148,7 @@ module Fixtures
|
|
144
148
|
file = self.send("#{m}_loc".to_sym)
|
145
149
|
|
146
150
|
dl_opts = {safe: true}
|
151
|
+
dl_opts[:contentType] = options[:contentType] if m == 'input' && options[:contentType]
|
147
152
|
RDF::Util::File.open_file(file, **dl_opts) do |remote_doc|
|
148
153
|
res = remote_doc.read
|
149
154
|
end
|
data/spec/to_rdf_spec.rb
CHANGED
@@ -1133,7 +1133,7 @@ describe JSON::LD::API do
|
|
1133
1133
|
],
|
1134
1134
|
"en-US rtl": [
|
1135
1135
|
%q({"http://example.org/label": {"@value": "en-US", "@language": "en-US", "@direction": "rtl"}}),
|
1136
|
-
%q(_:a <http://example.org/label> "en-US"^^<https://www.w3.org/ns/i18n#en-
|
1136
|
+
%q(_:a <http://example.org/label> "en-US"^^<https://www.w3.org/ns/i18n#en-us_rtl> .)
|
1137
1137
|
]
|
1138
1138
|
}.each do |title, (js, ttl)|
|
1139
1139
|
it title do
|
@@ -1161,7 +1161,7 @@ describe JSON::LD::API do
|
|
1161
1161
|
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
|
1162
1162
|
_:a <http://example.org/label> [
|
1163
1163
|
rdf:value "en-US";
|
1164
|
-
rdf:language "en-
|
1164
|
+
rdf:language "en-us";
|
1165
1165
|
rdf:direction "rtl"
|
1166
1166
|
] .
|
1167
1167
|
)
|
@@ -1218,7 +1218,7 @@ describe JSON::LD::API do
|
|
1218
1218
|
"@id": "http://example.com/foo",
|
1219
1219
|
"http://example.com/bar": {"@value": "bar", "@type": "http://example.com/baz z"}
|
1220
1220
|
}),
|
1221
|
-
|
1221
|
+
exception: JSON::LD::JsonLdError::InvalidTypedValue
|
1222
1222
|
},
|
1223
1223
|
"Injected IRIs check" => {
|
1224
1224
|
input: %({
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: json-ld
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.1.
|
4
|
+
version: 3.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gregg Kellogg
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-02-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rdf
|
@@ -64,14 +64,14 @@ dependencies:
|
|
64
64
|
requirements:
|
65
65
|
- - "~>"
|
66
66
|
- !ruby/object:Gem::Version
|
67
|
-
version: '0.
|
67
|
+
version: '0.2'
|
68
68
|
type: :runtime
|
69
69
|
prerelease: false
|
70
70
|
version_requirements: !ruby/object:Gem::Requirement
|
71
71
|
requirements:
|
72
72
|
- - "~>"
|
73
73
|
- !ruby/object:Gem::Version
|
74
|
-
version: '0.
|
74
|
+
version: '0.2'
|
75
75
|
- !ruby/object:Gem::Dependency
|
76
76
|
name: htmlentities
|
77
77
|
requirement: !ruby/object:Gem::Requirement
|
@@ -424,7 +424,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
424
424
|
- !ruby/object:Gem::Version
|
425
425
|
version: '0'
|
426
426
|
requirements: []
|
427
|
-
rubygems_version: 3.
|
427
|
+
rubygems_version: 3.1.2
|
428
428
|
signing_key:
|
429
429
|
specification_version: 4
|
430
430
|
summary: JSON-LD reader/writer for Ruby.
|