json-ld 0.9.1 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/{README.markdown → README.md} +15 -3
- data/VERSION +1 -1
- data/lib/json/ld.rb +50 -87
- data/lib/json/ld/api.rb +85 -96
- data/lib/json/ld/compact.rb +103 -170
- data/lib/json/ld/context.rb +1137 -0
- data/lib/json/ld/expand.rb +212 -171
- data/lib/json/ld/extensions.rb +17 -1
- data/lib/json/ld/flatten.rb +145 -78
- data/lib/json/ld/frame.rb +1 -1
- data/lib/json/ld/from_rdf.rb +73 -103
- data/lib/json/ld/reader.rb +3 -1
- data/lib/json/ld/resource.rb +3 -3
- data/lib/json/ld/to_rdf.rb +98 -109
- data/lib/json/ld/utils.rb +54 -4
- data/lib/json/ld/writer.rb +5 -5
- data/spec/api_spec.rb +3 -28
- data/spec/compact_spec.rb +76 -113
- data/spec/{evaluation_context_spec.rb → context_spec.rb} +307 -563
- data/spec/expand_spec.rb +163 -187
- data/spec/flatten_spec.rb +119 -114
- data/spec/frame_spec.rb +5 -5
- data/spec/from_rdf_spec.rb +44 -24
- data/spec/suite_compact_spec.rb +11 -8
- data/spec/suite_error_expand_spec.rb +23 -0
- data/spec/suite_expand_spec.rb +3 -7
- data/spec/suite_flatten_spec.rb +3 -3
- data/spec/suite_frame_spec.rb +6 -6
- data/spec/suite_from_rdf_spec.rb +3 -3
- data/spec/suite_helper.rb +13 -6
- data/spec/suite_to_rdf_spec.rb +16 -10
- data/spec/test-files/test-1-rdf.ttl +4 -3
- data/spec/test-files/test-3-rdf.ttl +2 -1
- data/spec/test-files/test-4-compacted.json +1 -1
- data/spec/test-files/test-5-rdf.ttl +3 -2
- data/spec/test-files/test-6-rdf.ttl +3 -2
- data/spec/test-files/test-7-compacted.json +3 -3
- data/spec/test-files/test-7-expanded.json +3 -3
- data/spec/test-files/test-7-rdf.ttl +7 -6
- data/spec/test-files/test-9-compacted.json +1 -1
- data/spec/to_rdf_spec.rb +67 -75
- data/spec/writer_spec.rb +2 -0
- metadata +36 -24
- checksums.yaml +0 -15
- data/lib/json/ld/evaluation_context.rb +0 -984
data/lib/json/ld/expand.rb
CHANGED
@@ -9,289 +9,330 @@ module JSON::LD
|
|
9
9
|
#
|
10
10
|
# @param [Array, Hash] input
|
11
11
|
# @param [String] active_property
|
12
|
-
# @param [
|
12
|
+
# @param [Context] context
|
13
13
|
# @param [Hash{Symbol => Object}] options
|
14
14
|
# @return [Array, Hash]
|
15
15
|
def expand(input, active_property, context, options = {})
|
16
16
|
debug("expand") {"input: #{input.inspect}, active_property: #{active_property.inspect}, context: #{context.inspect}"}
|
17
17
|
result = case input
|
18
18
|
when Array
|
19
|
-
# If element is an array,
|
20
|
-
# passing copies of the active context and active property. If the expanded entry is null, drop it.
|
19
|
+
# If element is an array,
|
21
20
|
depth do
|
22
21
|
is_list = context.container(active_property) == '@list'
|
23
22
|
value = input.map do |v|
|
24
|
-
#
|
25
|
-
|
26
|
-
# throw an exception as lists of lists are not allowed.
|
27
|
-
raise ProcessingError::ListOfLists, "A list may not contain another list" if v.is_a?(Array) && is_list
|
23
|
+
# Initialize expanded item to the result of using this algorithm recursively, passing active context, active property, and item as element.
|
24
|
+
v = expand(v, active_property, context, options)
|
28
25
|
|
29
|
-
|
26
|
+
# 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.
|
27
|
+
raise ProcessingError::ListOfLists,
|
28
|
+
"A list may not contain another list" if
|
29
|
+
is_list && (v.is_a?(Array) || list?(v))
|
30
|
+
v
|
30
31
|
end.flatten.compact
|
31
32
|
|
32
|
-
if is_list && value.any? {|v| v.is_a?(Hash) && v.has_key?('@list')}
|
33
|
-
raise ProcessingError::ListOfLists, "A list may not contain another list"
|
34
|
-
end
|
35
|
-
|
36
33
|
value
|
37
34
|
end
|
38
35
|
when Hash
|
39
|
-
#
|
40
|
-
# If element has a @context property, update the active context according to the steps outlined
|
41
|
-
# in Context Processing and remove the @context property.
|
36
|
+
# If element contains the key @context, set active context to the result of the Context Processing algorithm, passing active context and the value of the @context key as local context.
|
42
37
|
if input.has_key?('@context')
|
43
38
|
context = context.parse(input.delete('@context'))
|
44
|
-
debug("expand") {"
|
39
|
+
debug("expand") {"context: #{context.inspect}"}
|
45
40
|
end
|
46
41
|
|
47
42
|
depth do
|
48
43
|
output_object = Hash.ordered
|
49
44
|
# Then, proceed and process each property and value in element as follows:
|
50
|
-
input.keys.kw_sort.each do |
|
51
|
-
value
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
elsif expanded_property.is_a?(RDF::Resource)
|
59
|
-
expanded_property = expanded_property.to_s
|
60
|
-
end
|
45
|
+
input.keys.kw_sort.each do |key|
|
46
|
+
# For each key and value in element, ordered lexicographically by key:
|
47
|
+
value = input[key]
|
48
|
+
expanded_property = context.expand_iri(key, :vocab => true, :depth => @depth)
|
49
|
+
|
50
|
+
# If expanded property is null or it neither contains a colon (:) nor it is a keyword, drop key by continuing to the next key.
|
51
|
+
next if expanded_property.is_a?(RDF::URI) && expanded_property.relative?
|
52
|
+
expanded_property = expanded_property.to_s if expanded_property.is_a?(RDF::Resource)
|
61
53
|
|
62
54
|
debug("expand property") {"ap: #{active_property.inspect}, expanded: #{expanded_property.inspect}, value: #{value.inspect}"}
|
63
55
|
|
64
|
-
# If expanded property is an empty array, or null, continue with the next property from element
|
65
56
|
if expanded_property.nil?
|
66
57
|
debug(" => ") {"skip nil property"}
|
67
58
|
next
|
68
59
|
end
|
69
|
-
expanded_property
|
70
60
|
|
71
|
-
if
|
61
|
+
if KEYWORDS.include?(expanded_property)
|
62
|
+
# If active property equals @reverse, an invalid reverse property map error has been detected and processing is aborted.
|
63
|
+
raise ProcessingError::InvalidReversePropertyMap,
|
64
|
+
"@reverse not appropriate at this point" if active_property == '@reverse'
|
65
|
+
|
66
|
+
# If result has already an expanded property member, an colliding keywords error has been detected and processing is aborted.
|
67
|
+
raise ProcessingError::CollidingKeywords,
|
68
|
+
"#{expanded_property} already exists in result" if output_object.has_key?(expanded_property)
|
69
|
+
|
72
70
|
expanded_value = case expanded_property
|
73
71
|
when '@id'
|
74
|
-
# If expanded property is @id
|
75
|
-
|
72
|
+
# If expanded property is @id and value is not a string, an invalid @id value error has been detected and processing is aborted
|
73
|
+
raise ProcessingError::InvalidIdValue,
|
74
|
+
"value of @id must be a string: #{value.inspect}" unless value.is_a?(String)
|
75
|
+
|
76
|
+
# Otherwise, set expanded value to the result of using the IRI Expansion algorithm, passing active context, value, and true for document relative.
|
77
|
+
context.expand_iri(value, :documentRelative => true, :depth => @depth).to_s
|
76
78
|
when '@type'
|
77
|
-
# If expanded property is @type
|
79
|
+
# If expanded property is @type and value is neither a string nor an array of strings, an invalid type value error has been detected and processing is aborted. Otherwise, set expanded value to the result of using the IRI Expansion algorithm, passing active context, true for vocab, and true for document relative to expand the value or each of its items.
|
78
80
|
debug("@type") {"value: #{value.inspect}"}
|
79
81
|
case value
|
80
82
|
when Array
|
81
83
|
depth do
|
82
84
|
[value].flatten.map do |v|
|
83
|
-
|
84
|
-
|
85
|
-
context.expand_iri(v,
|
85
|
+
raise ProcessingError::InvalidTypeValue,
|
86
|
+
"@type value must be a string or array of strings: #{v.inspect}" unless v.is_a?(String)
|
87
|
+
context.expand_iri(v, :vocab => true, :documentRelative => true, :quiet => true, :depth => @depth).to_s
|
86
88
|
end
|
87
89
|
end
|
90
|
+
when String
|
91
|
+
context.expand_iri(value, :vocab => true, :documentRelative => true, :quiet => true, :depth => @depth).to_s
|
88
92
|
when Hash
|
89
|
-
#
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
raise ProcessingError, "Object value of @type must be empty or a node reference: #{value.inspect}"
|
94
|
-
else
|
95
|
-
value
|
96
|
-
end
|
93
|
+
# For framing
|
94
|
+
raise ProcessingError::InvalidTypeValue,
|
95
|
+
"@type value must be a an empty object for framing: #{value.inspect}" unless
|
96
|
+
value.empty?
|
97
97
|
else
|
98
|
-
|
98
|
+
raise ProcessingError::InvalidTypeValue,
|
99
|
+
"@type value must be a string or array of strings: #{value.inspect}"
|
99
100
|
end
|
101
|
+
when '@graph'
|
102
|
+
# 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.
|
103
|
+
depth { expand(value, '@graph', context, options) }
|
100
104
|
when '@value'
|
101
|
-
# If expanded property is @value
|
102
|
-
raise ProcessingError::
|
105
|
+
# 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.
|
106
|
+
raise ProcessingError::InvalidValueObjectValue,
|
107
|
+
"Value of #{expanded_property} must be a scalar or null: #{value.inspect}" if value.is_a?(Hash) || value.is_a?(Array)
|
108
|
+
if value.nil?
|
109
|
+
output_object['@value'] = nil
|
110
|
+
next;
|
111
|
+
end
|
103
112
|
value
|
104
113
|
when '@language'
|
105
|
-
# If expanded property is @language
|
106
|
-
raise ProcessingError::
|
107
|
-
|
114
|
+
# If expanded property is @language and value is not a string, an invalid language-tagged string error has been detected and processing is aborted. Otherwise, set expanded value to lowercased value.
|
115
|
+
raise ProcessingError::InvalidLanguageTaggedString,
|
116
|
+
"Value of #{expanded_property} must be a string: #{value.inspect}" unless value.is_a?(String)
|
117
|
+
value.downcase
|
108
118
|
when '@index'
|
109
|
-
# If expanded property is @index value
|
110
|
-
|
111
|
-
|
112
|
-
value
|
113
|
-
when '@list'
|
114
|
-
# If expanded property is @
|
119
|
+
# If expanded property is @index and value is not a string, an invalid @index value error has been detected and processing is aborted. Otherwise, set expanded value to value.
|
120
|
+
raise ProcessingError::InvalidIndexValue,
|
121
|
+
"Value of @index is not a string: #{value.inspect}" unless value.is_a?(String)
|
122
|
+
value
|
123
|
+
when '@list'
|
124
|
+
# If expanded property is @list:
|
125
|
+
|
126
|
+
# If active property is null or @graph, continue with the next key from element to remove the free-floating list.
|
127
|
+
next if (active_property || '@graph') == '@graph'
|
128
|
+
|
129
|
+
# Otherwise, initialize expanded value to the result of using this algorithm recursively passing active context, active property, and value for element.
|
130
|
+
value = depth { expand(value, active_property, context, options) }
|
131
|
+
|
132
|
+
# Spec FIXME: need to be sure that result is an array
|
115
133
|
value = [value] unless value.is_a?(Array)
|
116
|
-
ap = expanded_property == '@list' && ((active_property || '@graph') == '@graph') ? '@list' : active_property
|
117
|
-
value = depth { expand(value, ap, context, options) }
|
118
134
|
|
119
|
-
# If expanded
|
120
|
-
#
|
121
|
-
|
122
|
-
|
123
|
-
end
|
135
|
+
# If expanded value is a list object, a list of lists error has been detected and processing is aborted.
|
136
|
+
# Spec FIXME: Also look at each object if result is an array
|
137
|
+
raise ProcessingError::ListOfLists,
|
138
|
+
"A list may not contain another list" if value.any? {|v| list?(v)}
|
124
139
|
|
125
140
|
value
|
141
|
+
when '@set'
|
142
|
+
# 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.
|
143
|
+
depth { expand(value, active_property, context, options) }
|
144
|
+
when '@reverse'
|
145
|
+
# If expanded property is @reverse and value is not a JSON object, an invalid @reverse value error has been detected and processing is aborted.
|
146
|
+
raise ProcessingError::InvalidReverseValueError,
|
147
|
+
"@reverse value must be an object: #{value.inspect}" unless value.is_a?(Hash)
|
148
|
+
|
149
|
+
# Otherwise
|
150
|
+
# Initialize expanded value to the result of using this algorithm recursively, passing active context, @reverse as active property, and value as element.
|
151
|
+
value = depth { expand(value, '@reverse', context, options) }
|
152
|
+
|
153
|
+
# 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:
|
154
|
+
if value.has_key?('@reverse')
|
155
|
+
debug("@reverse") {"double reverse: #{value.inspect}"}
|
156
|
+
value['@reverse'].each do |property, item|
|
157
|
+
# If result does not have a property member, create one and set its value to an empty array.
|
158
|
+
# Append item to the value of the property member of result.
|
159
|
+
(output_object[property] ||= []).concat([item].flatten.compact)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
# If expanded value contains members other than @reverse:
|
164
|
+
unless value.keys.reject {|k| k == '@reverse'}.empty?
|
165
|
+
# If result does not have an @reverse member, create one and set its value to an empty JSON object.
|
166
|
+
reverse_map = output_object['@reverse'] ||= {}
|
167
|
+
value.each do |property, items|
|
168
|
+
next if property == '@reverse'
|
169
|
+
items.each do |item|
|
170
|
+
if value?(item) || list?(item)
|
171
|
+
raise ProcessingError::InvalidReversePropertyValue,
|
172
|
+
"invalid reverse property value: #{item.inspect}"
|
173
|
+
end
|
174
|
+
merge_value(reverse_map, property, item)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
# Continue with the next key from element
|
180
|
+
next
|
181
|
+
when '@explicit', '@default', '@embed', '@embedChildren', '@omitDefault'
|
182
|
+
# Framing keywords
|
183
|
+
depth { [expand(value, expanded_property, context, options)].flatten }
|
126
184
|
else
|
127
185
|
# Skip unknown keyword
|
128
186
|
next
|
129
187
|
end
|
130
188
|
|
189
|
+
# Unless expanded value is null, set the expanded property member of result to expanded value.
|
131
190
|
debug("expand #{expanded_property}") { expanded_value.inspect}
|
132
|
-
output_object[expanded_property] = expanded_value
|
191
|
+
output_object[expanded_property] = expanded_value unless expanded_value.nil?
|
133
192
|
next
|
134
193
|
end
|
135
194
|
|
136
|
-
expanded_value = if context.container(
|
137
|
-
# Otherwise, if
|
195
|
+
expanded_value = if context.container(key) == '@language' && value.is_a?(Hash)
|
196
|
+
# 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:
|
138
197
|
|
139
198
|
# Set multilingual array to an empty array.
|
140
|
-
|
199
|
+
ary = []
|
141
200
|
|
142
|
-
# For each key-value in
|
201
|
+
# For each key-value pair language-language value in value, ordered lexicographically by language
|
143
202
|
value.keys.sort.each do |k|
|
144
|
-
[value[k]].flatten.each do |
|
145
|
-
#
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
expanded_language_object['@language'] = k.downcase
|
155
|
-
# Append the expanded language object to the multilingual array.
|
156
|
-
language_map_values << expanded_language_object
|
203
|
+
[value[k]].flatten.each do |item|
|
204
|
+
# item must be a string, otherwise an invalid language map value error has been detected and processing is aborted.
|
205
|
+
raise ProcessingError::InvalidLanguageMapValue,
|
206
|
+
"Expected #{item.inspect} to be a string" unless item.is_a?(String)
|
207
|
+
|
208
|
+
# Append a JSON object to expanded value that consists of two key-value pairs: (@value-item) and (@language-lowercased language).
|
209
|
+
ary << {
|
210
|
+
'@value' => item,
|
211
|
+
'@language' => k.downcase
|
212
|
+
}
|
157
213
|
end
|
158
214
|
end
|
159
|
-
|
160
|
-
|
161
|
-
elsif context.container(
|
162
|
-
# Otherwise, if
|
215
|
+
|
216
|
+
ary
|
217
|
+
elsif context.container(key) == '@index' && value.is_a?(Hash)
|
218
|
+
# Otherwise, if key's container mapping in active context is @index and value is a JSON object then value is expanded from an index map as follows:
|
163
219
|
|
164
220
|
# Set ary to an empty array.
|
165
|
-
|
221
|
+
ary = []
|
166
222
|
|
167
223
|
# For each key-value in the object:
|
168
224
|
value.keys.sort.each do |k|
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
annotation_map_values << expanded_value
|
225
|
+
# Initialize index value to the result of using this algorithm recursively, passing active context, key as active property, and index value as element.
|
226
|
+
index_value = depth { expand([value[k]].flatten, key, context, options) }
|
227
|
+
index_value.each do |item|
|
228
|
+
item['@index'] ||= k
|
229
|
+
ary << item
|
175
230
|
end
|
176
231
|
end
|
177
|
-
|
178
|
-
annotation_map_values
|
232
|
+
ary
|
179
233
|
else
|
180
|
-
# Otherwise,
|
181
|
-
depth { expand(value,
|
234
|
+
# Otherwise, initialize expanded value to the result of using this algorithm recursively, passing active context, key for active property, and value for element.
|
235
|
+
depth { expand(value, key, context, options) }
|
182
236
|
end
|
183
237
|
|
184
|
-
#
|
238
|
+
# If expanded value is null, ignore key by continuing to the next key from element.
|
185
239
|
if expanded_value.nil?
|
186
240
|
debug(" => skip nil value")
|
187
241
|
next
|
188
242
|
end
|
243
|
+
debug {" => #{expanded_value.inspect}"}
|
189
244
|
|
190
|
-
# If
|
191
|
-
if context.container(
|
192
|
-
(!expanded_value.is_a?(Hash) || !expanded_value.fetch('@list', false))
|
193
|
-
|
245
|
+
# 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.
|
246
|
+
if context.container(key) == '@list' && !list?(expanded_value)
|
194
247
|
debug(" => ") { "convert #{expanded_value.inspect} to list"}
|
195
248
|
expanded_value = {'@list' => [expanded_value].flatten}
|
196
249
|
end
|
250
|
+
debug {" => #{expanded_value.inspect}"}
|
197
251
|
|
198
|
-
#
|
199
|
-
|
200
|
-
|
252
|
+
# Otherwise, if the term definition associated to key indicates that it is a reverse property
|
253
|
+
# Spec FIXME: this is not an otherwise.
|
254
|
+
if (td = context.term_definitions[key]) && td.reverse_property
|
255
|
+
# If result has no @reverse member, create one and initialize its value to an empty JSON object.
|
256
|
+
reverse_map = output_object['@reverse'] ||= {}
|
257
|
+
[expanded_value].flatten.each do |item|
|
258
|
+
# If item is a value object or list object, an invalid reverse property value has been detected and processing is aborted.
|
259
|
+
raise ProcessingError::InvalidReversePropertyValue,
|
260
|
+
"invalid reverse property value: #{item.inspect}" if value?(item) || list?(item)
|
201
261
|
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
# label all blank nodes in value with blank node identifiers by using the Label Blank Nodes Algorithm.
|
206
|
-
output_object[prop] ||= []
|
207
|
-
output_object[prop] += expanded_value.dup
|
262
|
+
# If reverse map has no expanded property member, create one and initialize its value to an empty array.
|
263
|
+
# Append item to the value of the expanded property member of reverse map.
|
264
|
+
merge_value(reverse_map, expanded_property, item)
|
208
265
|
end
|
209
266
|
else
|
210
|
-
if
|
211
|
-
|
212
|
-
|
213
|
-
else
|
214
|
-
# Otherwise, create a property property with value as value.
|
215
|
-
output_object[expanded_property] = expanded_value
|
216
|
-
end
|
267
|
+
# Otherwise, if key is not a reverse property:
|
268
|
+
# If result does not have an expanded property member, create one and initialize its value to an empty array.
|
269
|
+
(output_object[expanded_property] ||= []).concat([expanded_value].flatten)
|
217
270
|
end
|
218
|
-
debug {" => #{expanded_value.inspect}"}
|
219
271
|
end
|
220
272
|
|
221
273
|
debug("output object") {output_object.inspect}
|
222
274
|
|
223
|
-
# If
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
end
|
275
|
+
# If result contains the key @value:
|
276
|
+
if value?(output_object)
|
277
|
+
unless (output_object.keys - %w(@value @language @type @index)).empty?
|
278
|
+
# The result must not contain any keys other than @value, @language, @type, and @index. It must not contain both the @language key and the @type key. Otherwise, an invalid value object error has been detected and processing is aborted.
|
279
|
+
raise ProcessingError::InvalidValueObjectError,
|
280
|
+
"value object has unknown keys: #{output_object.inspect}"
|
281
|
+
end
|
231
282
|
|
232
|
-
# If the processed element has an @value property
|
233
|
-
if output_object.has_key?('@value')
|
234
283
|
output_object.delete('@language') if output_object['@language'].to_s.empty?
|
235
284
|
output_object.delete('@type') if output_object['@type'].to_s.empty?
|
236
|
-
if (%w(@index @language @type) - output_object.keys).empty?
|
237
|
-
raise ProcessingError, "element must not have more than one other property other than @index, which can either be @language or @type with a string value." unless value.is_a?(String)
|
238
|
-
end
|
239
285
|
|
240
|
-
#
|
286
|
+
# If the value of result's @value key is null, then set result to null.
|
241
287
|
return nil if output_object['@value'].nil?
|
288
|
+
|
289
|
+
if !output_object['@value'].is_a?(String) && output_object.has_key?('@language')
|
290
|
+
# 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.
|
291
|
+
raise ProcessingError::InvalidLanguageTaggedValue,
|
292
|
+
"when @language is used, @value must be a string: #{@value.inspect}"
|
293
|
+
elsif !output_object.fetch('@type', "").is_a?(String)
|
294
|
+
# Otherwise, if the result has a @type member and its value is not a string, an invalid typed value error has been detected and processing is aborted.
|
295
|
+
raise ProcessingError::InvalidTypedValue,
|
296
|
+
"value of @type must be a string: #{output_object['@type'].inspect}"
|
297
|
+
end
|
242
298
|
elsif !output_object.fetch('@type', []).is_a?(Array)
|
243
|
-
# Otherwise, if
|
244
|
-
# convert it to an array.
|
299
|
+
# Otherwise, if result contains the key @type and its associated value is not an array, set it to an array containing only the associated value.
|
245
300
|
output_object['@type'] = [output_object['@type']]
|
301
|
+
elsif output_object.keys.any? {|k| %w(@set @list).include?(k)}
|
302
|
+
# Otherwise, if result contains the key @set or @list:
|
303
|
+
# The result must contain at most one other key and that key must be @index. Otherwise, an invalid set or list object error has been detected and processing is aborted.
|
304
|
+
raise ProcessingError::InvalidSetOrListObject,
|
305
|
+
"@set or @list may only contain @index: #{output_object.keys.inspect}" unless
|
306
|
+
(output_object.keys - %w(@set @list @index)).empty?
|
307
|
+
|
308
|
+
# If result contains the key @set, then set result to the key's associated value.
|
309
|
+
return output_object['@set'] if output_object.keys.include?('@set')
|
246
310
|
end
|
247
311
|
|
248
|
-
# If
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
312
|
+
# If result contains only the key @language, set result to null.
|
313
|
+
return nil if output_object.keys == %w(@language)
|
314
|
+
|
315
|
+
# If active property is null or @graph, drop free-floating values as follows:
|
316
|
+
if (active_property || '@graph') == '@graph' &&
|
317
|
+
(output_object.keys.any? {|k| %w(@value @list).include?(k)} ||
|
318
|
+
(output_object.keys - %w(@id)).empty?)
|
319
|
+
debug(" =>") { "empty top-level: " + output_object.inspect}
|
320
|
+
return nil
|
255
321
|
end
|
256
322
|
|
257
323
|
# Re-order result keys
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
elsif output_object.is_a?(Hash)
|
262
|
-
r = Hash.ordered
|
263
|
-
output_object.keys.kw_sort.each {|k| r[k] = output_object[k]}
|
264
|
-
r
|
265
|
-
else
|
266
|
-
output_object
|
267
|
-
end
|
324
|
+
r = Hash.ordered
|
325
|
+
output_object.keys.kw_sort.each {|k| r[k] = output_object[k]}
|
326
|
+
r
|
268
327
|
end
|
269
328
|
else
|
270
329
|
# Otherwise, unless the value is a number, expand the value according to the Value Expansion rules, passing active property.
|
271
|
-
|
272
|
-
|
273
|
-
) unless input.nil? || active_property.nil? || active_property == '@graph'
|
330
|
+
return nil if input.nil? || active_property.nil? || active_property == '@graph'
|
331
|
+
context.expand_value(active_property, input, :depth => @depth)
|
274
332
|
end
|
275
333
|
|
276
334
|
debug {" => #{result.inspect}"}
|
277
335
|
result
|
278
336
|
end
|
279
|
-
|
280
|
-
protected
|
281
|
-
# @param [Array, Hash] input
|
282
|
-
def label_blanknodes(element)
|
283
|
-
if element.is_a?(Array)
|
284
|
-
element.each {|e| label_blanknodes(e)}
|
285
|
-
elsif list?(element)
|
286
|
-
element['@list'].each {|e| label_blanknodes(e)}
|
287
|
-
elsif element.is_a?(Hash)
|
288
|
-
element.keys.sort.each do |k|
|
289
|
-
label_blanknodes(element[k])
|
290
|
-
end
|
291
|
-
if node?(element) and !element.has_key?('@id')
|
292
|
-
element['@id'] = namer.get_name(nil)
|
293
|
-
end
|
294
|
-
end
|
295
|
-
end
|
296
337
|
end
|
297
338
|
end
|