json-ld 3.1.7 → 3.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +23 -10
- data/VERSION +1 -1
- data/bin/jsonld +13 -11
- data/lib/json/ld/api.rb +34 -17
- data/lib/json/ld/compact.rb +6 -5
- data/lib/json/ld/context.rb +63 -57
- data/lib/json/ld/expand.rb +57 -19
- data/lib/json/ld/extensions.rb +4 -4
- data/lib/json/ld/flatten.rb +137 -9
- data/lib/json/ld/format.rb +8 -1
- data/lib/json/ld/frame.rb +8 -8
- data/lib/json/ld/from_rdf.rb +14 -8
- data/lib/json/ld/html/nokogiri.rb +3 -4
- data/lib/json/ld/reader.rb +1 -0
- data/lib/json/ld/streaming_reader.rb +5 -5
- data/lib/json/ld/streaming_writer.rb +4 -4
- data/lib/json/ld/to_rdf.rb +2 -2
- data/lib/json/ld/utils.rb +13 -13
- data/lib/json/ld/writer.rb +2 -2
- data/lib/json/ld.rb +3 -1
- data/spec/api_spec.rb +1 -1
- data/spec/compact_spec.rb +72 -2
- data/spec/context_spec.rb +8 -3
- data/spec/expand_spec.rb +385 -2
- data/spec/flatten_spec.rb +517 -1
- data/spec/frame_spec.rb +93 -3
- data/spec/from_rdf_spec.rb +1 -1
- data/spec/rdfstar_spec.rb +25 -0
- data/spec/spec_helper.rb +40 -2
- data/spec/suite_flatten_spec.rb +4 -0
- data/spec/suite_frame_spec.rb +7 -0
- data/spec/suite_helper.rb +13 -7
- data/spec/suite_to_rdf_spec.rb +1 -1
- data/spec/to_rdf_spec.rb +4 -4
- data/spec/writer_spec.rb +1 -1
- metadata +88 -86
data/lib/json/ld/extensions.rb
CHANGED
@@ -11,10 +11,10 @@ module RDF
|
|
11
11
|
class Statement
|
12
12
|
# Validate extended RDF
|
13
13
|
def valid_extended?
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
(
|
14
|
+
subject? && subject.resource? && subject.valid_extended? &&
|
15
|
+
predicate? && predicate.resource? && predicate.valid_extended? &&
|
16
|
+
object? && object.term? && object.valid_extended? &&
|
17
|
+
(graph? ? (graph_name.resource? && graph_name.valid_extended?) : true)
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
data/lib/json/ld/flatten.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
2
|
# frozen_string_literal: true
|
3
|
+
require 'json/canonicalization'
|
4
|
+
|
3
5
|
module JSON::LD
|
4
6
|
module Flatten
|
5
7
|
include Utils
|
@@ -7,6 +9,10 @@ module JSON::LD
|
|
7
9
|
##
|
8
10
|
# This algorithm creates a JSON object node map holding an indexed representation of the graphs and nodes represented in the passed expanded document. All nodes that are not uniquely identified by an IRI get assigned a (new) blank node identifier. The resulting node map will have a member for every graph in the document whose value is another object with a member for every node represented in the document. The default graph is stored under the @default member, all other graphs are stored under their graph name.
|
9
11
|
#
|
12
|
+
# For RDF-star/JSON-LD-star:
|
13
|
+
# * Values of `@id` can be an object (embedded node); when these are used as keys in a Node Map, they are serialized as canonical JSON, and de-serialized when flattening.
|
14
|
+
# * The presence of `@annotation` implies an embedded node and the annotation object is removed from the node/value object in which it appears.
|
15
|
+
#
|
10
16
|
# @param [Array, Hash] element
|
11
17
|
# Expanded JSON-LD input
|
12
18
|
# @param [Hash] graph_map A map of graph name to subjects
|
@@ -16,12 +22,15 @@ module JSON::LD
|
|
16
22
|
# Node identifier
|
17
23
|
# @param [String] active_property (nil)
|
18
24
|
# Property within current node
|
25
|
+
# @param [Boolean] reverse (false)
|
26
|
+
# Processing a reverse relationship
|
19
27
|
# @param [Array] list (nil)
|
20
28
|
# Used when property value is a list
|
21
29
|
def create_node_map(element, graph_map,
|
22
30
|
active_graph: '@default',
|
23
31
|
active_subject: nil,
|
24
32
|
active_property: nil,
|
33
|
+
reverse: false,
|
25
34
|
list: nil)
|
26
35
|
if element.is_a?(Array)
|
27
36
|
# If element is an array, process each entry in element recursively by passing item for element, node map, active graph, active subject, active property, and list.
|
@@ -30,21 +39,45 @@ module JSON::LD
|
|
30
39
|
active_graph: active_graph,
|
31
40
|
active_subject: active_subject,
|
32
41
|
active_property: active_property,
|
42
|
+
reverse: false,
|
33
43
|
list: list)
|
34
44
|
end
|
35
45
|
elsif !element.is_a?(Hash)
|
36
46
|
raise "Expected hash or array to create_node_map, got #{element.inspect}"
|
37
47
|
else
|
38
48
|
graph = (graph_map[active_graph] ||= {})
|
39
|
-
subject_node = graph[active_subject]
|
49
|
+
subject_node = !reverse && graph[active_subject.is_a?(Hash) ? active_subject.to_json_c14n : active_subject]
|
40
50
|
|
41
51
|
# Transform BNode types
|
42
|
-
if element.
|
52
|
+
if element.key?('@type')
|
43
53
|
element['@type'] = Array(element['@type']).map {|t| blank_node?(t) ? namer.get_name(t) : t}
|
44
54
|
end
|
45
55
|
|
46
56
|
if value?(element)
|
47
57
|
element['@type'] = element['@type'].first if element ['@type']
|
58
|
+
|
59
|
+
# For rdfstar, if value contains an `@annotation` member ...
|
60
|
+
# note: active_subject will not be nil, and may be an object itself.
|
61
|
+
if element.key?('@annotation')
|
62
|
+
# rdfstar being true is implicit, as it is checked in expansion
|
63
|
+
as = node_reference?(active_subject) ?
|
64
|
+
active_subject['@id'] :
|
65
|
+
active_subject
|
66
|
+
star_subject = {
|
67
|
+
"@id" => as,
|
68
|
+
active_property => [element]
|
69
|
+
}
|
70
|
+
|
71
|
+
# Note that annotation is an array, make the reified subject the id of each member of that array.
|
72
|
+
annotation = element.delete('@annotation').map do |a|
|
73
|
+
a.merge('@id' => star_subject)
|
74
|
+
end
|
75
|
+
|
76
|
+
# Invoke recursively using annotation.
|
77
|
+
create_node_map(annotation, graph_map,
|
78
|
+
active_graph: active_graph)
|
79
|
+
end
|
80
|
+
|
48
81
|
if list.nil?
|
49
82
|
add_value(subject_node, active_property, element, property_is_array: true, allow_duplicate: false)
|
50
83
|
else
|
@@ -64,13 +97,19 @@ module JSON::LD
|
|
64
97
|
end
|
65
98
|
else
|
66
99
|
# Element is a node object
|
67
|
-
id = element.delete('@id')
|
68
|
-
id
|
100
|
+
ser_id = id = element.delete('@id')
|
101
|
+
if id.is_a?(Hash)
|
102
|
+
# Index graph using serialized id
|
103
|
+
ser_id = id.to_json_c14n
|
104
|
+
elsif id.nil?
|
105
|
+
ser_id = id = namer.get_name
|
106
|
+
end
|
69
107
|
|
70
|
-
node = graph[
|
108
|
+
node = graph[ser_id] ||= {'@id' => id}
|
71
109
|
|
72
|
-
if
|
73
|
-
#
|
110
|
+
if reverse
|
111
|
+
# Note: active_subject is a Hash
|
112
|
+
# We're processing a reverse-property relationship.
|
74
113
|
add_value(node, active_property, active_subject, property_is_array: true, allow_duplicate: false)
|
75
114
|
elsif active_property
|
76
115
|
reference = {'@id' => id}
|
@@ -81,7 +120,30 @@ module JSON::LD
|
|
81
120
|
end
|
82
121
|
end
|
83
122
|
|
84
|
-
if
|
123
|
+
# For rdfstar, if node contains an `@annotation` member ...
|
124
|
+
# note: active_subject will not be nil, and may be an object itself.
|
125
|
+
# XXX: what if we're reversing an annotation?
|
126
|
+
if element.key?('@annotation')
|
127
|
+
# rdfstar being true is implicit, as it is checked in expansion
|
128
|
+
as = node_reference?(active_subject) ?
|
129
|
+
active_subject['@id'] :
|
130
|
+
active_subject
|
131
|
+
star_subject = reverse ?
|
132
|
+
{"@id" => node['@id'], active_property => [{'@id' => as}]} :
|
133
|
+
{"@id" => as, active_property => [{'@id' => node['@id']}]}
|
134
|
+
|
135
|
+
# Note that annotation is an array, make the reified subject the id of each member of that array.
|
136
|
+
annotation = element.delete('@annotation').map do |a|
|
137
|
+
a.merge('@id' => star_subject)
|
138
|
+
end
|
139
|
+
|
140
|
+
# Invoke recursively using annotation.
|
141
|
+
create_node_map(annotation, graph_map,
|
142
|
+
active_graph: active_graph,
|
143
|
+
active_subject: star_subject)
|
144
|
+
end
|
145
|
+
|
146
|
+
if element.key?('@type')
|
85
147
|
add_value(node, '@type', element.delete('@type'), property_is_array: true, allow_duplicate: false)
|
86
148
|
end
|
87
149
|
|
@@ -99,7 +161,8 @@ module JSON::LD
|
|
99
161
|
create_node_map(value, graph_map,
|
100
162
|
active_graph: active_graph,
|
101
163
|
active_subject: referenced_node,
|
102
|
-
active_property: property
|
164
|
+
active_property: property,
|
165
|
+
reverse: true)
|
103
166
|
end
|
104
167
|
end
|
105
168
|
end
|
@@ -128,7 +191,72 @@ module JSON::LD
|
|
128
191
|
end
|
129
192
|
end
|
130
193
|
|
194
|
+
##
|
195
|
+
# Create annotations
|
196
|
+
#
|
197
|
+
# Updates a node map from which annotations have been folded into embedded triples to re-extract the annotations.
|
198
|
+
#
|
199
|
+
# Map entries where the key is of the form of a canonicalized JSON object are used to find keys with the `@id` and property components. If found, the original map entry is removed and entries added to an `@annotation` property of the associated value.
|
200
|
+
#
|
201
|
+
# * Keys which are of the form of a canonicalized JSON object are examined in inverse order of length.
|
202
|
+
# * Deserialize the key into a map, and re-serialize the value of `@id`.
|
203
|
+
# * If the map contains an entry with that value (after re-canonicalizing, as appropriate), and the associated antry has a item which matches the non-`@id` item from the map, the node is used to create an `@annotation` entry within that value.
|
204
|
+
#
|
205
|
+
# @param [Hash{String => Hash}] node_map
|
206
|
+
# @return [Hash{String => Hash}]
|
207
|
+
def create_annotations(node_map)
|
208
|
+
node_map.keys.
|
209
|
+
select {|k| k.start_with?('{')}.
|
210
|
+
sort_by(&:length).
|
211
|
+
reverse.each do |key|
|
212
|
+
|
213
|
+
annotation = node_map[key]
|
214
|
+
# Deserialize key, and re-serialize the `@id` value.
|
215
|
+
emb = annotation['@id'].dup
|
216
|
+
id = emb.delete('@id')
|
217
|
+
property, value = emb.to_a.first
|
218
|
+
|
219
|
+
# If id is a map, set it to the result of canonicalizing that value, otherwise to itself.
|
220
|
+
id = id.to_json_c14n if id.is_a?(Hash)
|
221
|
+
|
222
|
+
next unless node_map.key?(id)
|
223
|
+
# If node map has an entry for id and that entry contains the same property and value from entry:
|
224
|
+
node = node_map[id]
|
225
|
+
|
226
|
+
next unless node.key?(property)
|
227
|
+
|
228
|
+
node[property].each do |emb_value|
|
229
|
+
next unless emb_value == value.first
|
230
|
+
|
231
|
+
node_map.delete(key)
|
232
|
+
annotation.delete('@id')
|
233
|
+
add_value(emb_value, '@annotation', annotation, property_is_array: true) unless
|
234
|
+
annotation.empty?
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
##
|
240
|
+
# Rename blank nodes recursively within an embedded object
|
241
|
+
#
|
242
|
+
# @param [Object] node
|
243
|
+
# @return [Hash]
|
244
|
+
def rename_bnodes(node)
|
245
|
+
case node
|
246
|
+
when Array
|
247
|
+
node.map {|n| rename_bnodes(n)}
|
248
|
+
when Hash
|
249
|
+
node.inject({}) do |memo, (k, v)|
|
250
|
+
v = namer.get_name(v) if k == '@id' && v.is_a?(String) && blank_node?(v)
|
251
|
+
memo.merge(k => rename_bnodes(v))
|
252
|
+
end
|
253
|
+
else
|
254
|
+
node
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
131
258
|
private
|
259
|
+
|
132
260
|
##
|
133
261
|
# Merge nodes from all graphs in the graph_map into a new node map
|
134
262
|
#
|
data/lib/json/ld/format.rb
CHANGED
@@ -57,7 +57,7 @@ module JSON::LD
|
|
57
57
|
lambda: ->(files, **options) do
|
58
58
|
out = options[:output] || $stdout
|
59
59
|
out.set_encoding(Encoding::UTF_8) if RUBY_PLATFORM == "java"
|
60
|
-
options = options.merge(expandContext: options.delete(:context)) if options.
|
60
|
+
options = options.merge(expandContext: options.delete(:context)) if options.key?(:context)
|
61
61
|
options[:base] ||= options[:base_uri]
|
62
62
|
if options[:format] == :jsonld
|
63
63
|
if files.empty?
|
@@ -174,6 +174,13 @@ module JSON::LD
|
|
174
174
|
use: :required,
|
175
175
|
on: ["--context CONTEXT"],
|
176
176
|
description: "Context to use when compacting.") {|arg| RDF::URI(arg)},
|
177
|
+
RDF::CLI::Option.new(
|
178
|
+
symbol: :createAnnotations,
|
179
|
+
datatype: TrueClass,
|
180
|
+
default: false,
|
181
|
+
control: :checkbox,
|
182
|
+
on: ["--[no-]create-annotations"],
|
183
|
+
description: "Unfold embedded nodes which can be represented using `@annotation`."),
|
177
184
|
]
|
178
185
|
},
|
179
186
|
frame: {
|
data/lib/json/ld/frame.rb
CHANGED
@@ -52,7 +52,7 @@ module JSON::LD
|
|
52
52
|
state[:uniqueEmbeds][state[:graph]] ||= {}
|
53
53
|
end
|
54
54
|
|
55
|
-
if flags[:embed] == '@link' && link.
|
55
|
+
if flags[:embed] == '@link' && link.key?(id)
|
56
56
|
# add existing linked subject
|
57
57
|
add_frame_output(parent, property, link[id])
|
58
58
|
next
|
@@ -66,7 +66,7 @@ module JSON::LD
|
|
66
66
|
warn "[DEPRECATION] #{flags[:embed]} is not a valid value of @embed in 1.1 mode.\n"
|
67
67
|
end
|
68
68
|
|
69
|
-
if !state[:embedded] && state[:uniqueEmbeds][state[:graph]].
|
69
|
+
if !state[:embedded] && state[:uniqueEmbeds][state[:graph]].key?(id)
|
70
70
|
# Skip adding this node object to the top-level, as it was included in another node object
|
71
71
|
next
|
72
72
|
elsif state[:embedded] &&
|
@@ -76,7 +76,7 @@ module JSON::LD
|
|
76
76
|
next
|
77
77
|
elsif state[:embedded] &&
|
78
78
|
%w(@first @once).include?(flags[:embed]) &&
|
79
|
-
state[:uniqueEmbeds][state[:graph]].
|
79
|
+
state[:uniqueEmbeds][state[:graph]].key?(id)
|
80
80
|
|
81
81
|
# if only the first match should be embedded
|
82
82
|
# Embed unless already embedded
|
@@ -97,7 +97,7 @@ module JSON::LD
|
|
97
97
|
state[:subjectStack] << {subject: subject, graph: state[:graph]}
|
98
98
|
|
99
99
|
# Subject is also the name of a graph
|
100
|
-
if state[:graphMap].
|
100
|
+
if state[:graphMap].key?(id)
|
101
101
|
# check frame's "@graph" to see what to do next
|
102
102
|
# 1. if it doesn't exist and state.graph === "@merged", don't recurse
|
103
103
|
# 2. if it doesn't exist and state.graph !== "@merged", recurse
|
@@ -105,7 +105,7 @@ module JSON::LD
|
|
105
105
|
# 4. if "@default" then don't recurse
|
106
106
|
# 5. recurse
|
107
107
|
recurse, subframe = false, nil
|
108
|
-
if !frame.
|
108
|
+
if !frame.key?('@graph')
|
109
109
|
recurse, subframe = (state[:graph] != '@merged'), {}
|
110
110
|
else
|
111
111
|
subframe = frame['@graph'].first
|
@@ -134,7 +134,7 @@ module JSON::LD
|
|
134
134
|
end
|
135
135
|
|
136
136
|
# explicit is on and property isn't in frame, skip processing
|
137
|
-
next if flags[:explicit] && !frame.
|
137
|
+
next if flags[:explicit] && !frame.key?(prop)
|
138
138
|
|
139
139
|
# add objects
|
140
140
|
objects.each do |o|
|
@@ -267,7 +267,7 @@ module JSON::LD
|
|
267
267
|
# If, after replacement, an array contains only the value null remove the value, leaving an empty array.
|
268
268
|
input.map {|o| cleanup_preserve(o)}
|
269
269
|
when Hash
|
270
|
-
if input.
|
270
|
+
if input.key?('@preserve')
|
271
271
|
# Replace with the content of `@preserve`
|
272
272
|
cleanup_preserve(input['@preserve'].first)
|
273
273
|
else
|
@@ -388,7 +388,7 @@ module JSON::LD
|
|
388
388
|
is_empty = v.empty?
|
389
389
|
if v = v.first
|
390
390
|
validate_frame(v)
|
391
|
-
has_default = v.
|
391
|
+
has_default = v.key?('@default')
|
392
392
|
end
|
393
393
|
|
394
394
|
# No longer a wildcard pattern if frame has any non-keyword properties
|
data/lib/json/ld/from_rdf.rb
CHANGED
@@ -40,8 +40,10 @@ module JSON::LD
|
|
40
40
|
|
41
41
|
default_graph[name] ||= {'@id' => name} unless name == '@default'
|
42
42
|
|
43
|
-
subject = statement.subject.
|
44
|
-
|
43
|
+
subject = statement.subject.statement? ?
|
44
|
+
resource_representation(statement.subject, useNativeTypes)['@id'].to_json_c14n :
|
45
|
+
statement.subject.to_s
|
46
|
+
node = node_map[subject] ||= resource_representation(statement.subject, useNativeTypes)
|
45
47
|
|
46
48
|
# If predicate is rdf:datatype, note subject in compound literal subjects map
|
47
49
|
if @options[:rdfDirection] == 'compound-literal' && statement.predicate == RDF.to_uri + 'direction'
|
@@ -50,12 +52,14 @@ module JSON::LD
|
|
50
52
|
|
51
53
|
# If object is an IRI, blank node identifier, or statement, and node map does not have an object member, create one and initialize its value to a new JSON object consisting of a single member @id whose value is set to object.
|
52
54
|
unless statement.object.literal?
|
53
|
-
|
55
|
+
object = statement.object.statement? ?
|
56
|
+
resource_representation(statement.object, useNativeTypes)['@id'].to_json_c14n :
|
57
|
+
statement.object.to_s
|
58
|
+
node_map[object] ||=
|
54
59
|
resource_representation(statement.object, useNativeTypes)
|
55
60
|
end
|
56
61
|
|
57
62
|
# If predicate equals rdf:type, and object is an IRI or blank node identifier, append object to the value of the @type member of node. If no such member exists, create one and initialize it to an array whose only item is object. Finally, continue to the next RDF triple.
|
58
|
-
# XXX JSON-LD* does not support embedded value of @type
|
59
63
|
if statement.predicate == RDF.type && statement.object.resource? && !useRdfType
|
60
64
|
merge_value(node, '@type', statement.object.to_s)
|
61
65
|
next
|
@@ -75,7 +79,7 @@ module JSON::LD
|
|
75
79
|
property: statement.predicate.to_s,
|
76
80
|
value: value
|
77
81
|
})
|
78
|
-
elsif referenced_once.
|
82
|
+
elsif referenced_once.key?(statement.object.to_s)
|
79
83
|
referenced_once[statement.object.to_s] = false
|
80
84
|
elsif statement.object.node?
|
81
85
|
referenced_once[statement.object.to_s] = {
|
@@ -112,8 +116,7 @@ module JSON::LD
|
|
112
116
|
end
|
113
117
|
end
|
114
118
|
|
115
|
-
|
116
|
-
next unless nil_var = graph_object[RDF.nil.to_s]
|
119
|
+
nil_var = graph_object.fetch(RDF.nil.to_s, {})
|
117
120
|
|
118
121
|
# For each item usage in the usages member of nil, perform the following steps:
|
119
122
|
nil_var.fetch(:usages, []).each do |usage|
|
@@ -141,12 +144,15 @@ module JSON::LD
|
|
141
144
|
head['@list'] = list.reverse
|
142
145
|
list_nodes.each {|node_id| graph_object.delete(node_id)}
|
143
146
|
end
|
147
|
+
|
148
|
+
# Create annotations on graph object
|
149
|
+
create_annotations(graph_object)
|
144
150
|
end
|
145
151
|
|
146
152
|
result = []
|
147
153
|
default_graph.keys.opt_sort(ordered: @options[:ordered]).each do |subject|
|
148
154
|
node = default_graph[subject]
|
149
|
-
if graph_map.
|
155
|
+
if graph_map.key?(subject)
|
150
156
|
node['@graph'] = []
|
151
157
|
graph_map[subject].keys.opt_sort(ordered: @options[:ordered]).each do |s|
|
152
158
|
n = graph_map[subject][s]
|
@@ -136,11 +136,10 @@ module JSON::LD
|
|
136
136
|
input
|
137
137
|
else
|
138
138
|
begin
|
139
|
-
require 'nokogumbo' unless defined?(::Nokogumbo)
|
140
139
|
input = input.read if input.respond_to?(:read)
|
141
|
-
::Nokogiri::HTML5(input.
|
142
|
-
rescue LoadError
|
143
|
-
::Nokogiri::HTML.parse(input, 'utf-8')
|
140
|
+
::Nokogiri::HTML5(input.force_encoding('utf-8'), max_parse_errors: 1000)
|
141
|
+
rescue LoadError, NoMethodError
|
142
|
+
::Nokogiri::HTML.parse(input, base_uri.to_s, 'utf-8')
|
144
143
|
end
|
145
144
|
end
|
146
145
|
|
data/lib/json/ld/reader.rb
CHANGED
@@ -68,6 +68,7 @@ module JSON::LD
|
|
68
68
|
# @raise [RDF::ReaderError] if the JSON document cannot be loaded
|
69
69
|
def initialize(input = $stdin, **options, &block)
|
70
70
|
options[:base_uri] ||= options[:base]
|
71
|
+
options[:rename_bnodes] = false unless options.key?(:rename_bnodes)
|
71
72
|
super do
|
72
73
|
@options[:base] ||= base_uri.to_s if base_uri
|
73
74
|
# Trim non-JSON stuff in script.
|
@@ -136,7 +136,7 @@ module JSON::LD
|
|
136
136
|
when '@type'
|
137
137
|
# Set the type-scoped context to the context on input, for use later
|
138
138
|
raise JsonLdError::InvalidStreamingKeyOrder,
|
139
|
-
"found #{key} in state #{state}" unless
|
139
|
+
"found #{key} in state #{state}" unless %i(await_context await_type).include?(state)
|
140
140
|
|
141
141
|
type_scoped_context = context
|
142
142
|
as_array(value).sort.each do |term|
|
@@ -159,7 +159,7 @@ module JSON::LD
|
|
159
159
|
raise JsonLdError::InvalidSetOrListObject,
|
160
160
|
"found #{key} in state #{state}" if is_list_or_set
|
161
161
|
raise JsonLdError::CollidingKeywords,
|
162
|
-
"found #{key} in state #{state}" unless
|
162
|
+
"found #{key} in state #{state}" unless %i(await_context await_type await_id).include?(state)
|
163
163
|
|
164
164
|
# Set our actual id, and use for replacing any provisional statements using our existing node_id, which is provisional
|
165
165
|
raise JsonLdError::InvalidIdValue,
|
@@ -209,7 +209,7 @@ module JSON::LD
|
|
209
209
|
# Expanded values must be node objects
|
210
210
|
have_statements = false
|
211
211
|
parse_object(value, active_property, context) do |st|
|
212
|
-
have_statements ||= st.
|
212
|
+
have_statements ||= st.subject?
|
213
213
|
block.call(st)
|
214
214
|
end
|
215
215
|
raise JsonLdError::InvalidIncludedValue, "values of @included must expand to node objects" unless have_statements
|
@@ -232,7 +232,7 @@ module JSON::LD
|
|
232
232
|
when '@list'
|
233
233
|
raise JsonLdError::InvalidSetOrListObject,
|
234
234
|
"found #{key} in state #{state}" if
|
235
|
-
|
235
|
+
!%i(await_context await_type await_id).include?(state)
|
236
236
|
is_list_or_set = true
|
237
237
|
if subject
|
238
238
|
node_id = parse_list(value, active_property, context, &block)
|
@@ -277,7 +277,7 @@ module JSON::LD
|
|
277
277
|
when '@set'
|
278
278
|
raise JsonLdError::InvalidSetOrListObject,
|
279
279
|
"found #{key} in state #{state}" if
|
280
|
-
|
280
|
+
!%i(await_context await_type await_id).include?(state)
|
281
281
|
is_list_or_set = true
|
282
282
|
value = as_array(value).compact
|
283
283
|
parse_object(value, active_property, context, subject: subject, predicate: predicate, &block)
|
@@ -64,8 +64,8 @@ module JSON::LD
|
|
64
64
|
{"@value" => MultiJson.load(statement.object.to_s), "@type" => "@json"}
|
65
65
|
else
|
66
66
|
lit = {"@value" => statement.object.to_s}
|
67
|
-
lit["@type"] = statement.object.datatype.to_s if statement.object.
|
68
|
-
lit["@language"] = statement.object.language.to_s if statement.object.
|
67
|
+
lit["@type"] = statement.object.datatype.to_s if statement.object.datatype?
|
68
|
+
lit["@language"] = statement.object.language.to_s if statement.object.language?
|
69
69
|
lit
|
70
70
|
end
|
71
71
|
end
|
@@ -91,7 +91,7 @@ module JSON::LD
|
|
91
91
|
def start_graph(resource)
|
92
92
|
#log_debug("start_graph") {"state: #{@state.inspect}, resource: #{resource}"}
|
93
93
|
if resource
|
94
|
-
@output.puts(",") if
|
94
|
+
@output.puts(",") if %i(wrote_node wrote_graph).include?(@state)
|
95
95
|
@output.puts %({"@id": "#{resource}", "@graph": [)
|
96
96
|
@state = :in_graph
|
97
97
|
end
|
@@ -109,7 +109,7 @@ module JSON::LD
|
|
109
109
|
|
110
110
|
def end_node
|
111
111
|
#log_debug("end_node") {"state: #{@state.inspect}, node: #{@current_node_def.to_json}"}
|
112
|
-
@output.puts(",") if
|
112
|
+
@output.puts(",") if %i(wrote_node wrote_graph).include?(@state)
|
113
113
|
if @current_node_def
|
114
114
|
node_def = if context
|
115
115
|
compacted = JSON::LD::API.compact(@current_node_def, context, rename_bnodes: false, **@options)
|
data/lib/json/ld/to_rdf.rb
CHANGED
@@ -48,7 +48,7 @@ module JSON::LD
|
|
48
48
|
# Only valid for rdf:JSON
|
49
49
|
value = value.to_json_c14n
|
50
50
|
else
|
51
|
-
if item.
|
51
|
+
if item.key?('@direction') && @options[:rdfDirection]
|
52
52
|
# Either serialize using a datatype, or a compound-literal
|
53
53
|
case @options[:rdfDirection]
|
54
54
|
when 'i18n-datatype'
|
@@ -63,7 +63,7 @@ module JSON::LD
|
|
63
63
|
end
|
64
64
|
|
65
65
|
# Otherwise, if datatype is null, set it to xsd:string or xsd:langString, depending on if item has a @language key.
|
66
|
-
datatype ||= item.
|
66
|
+
datatype ||= item.key?('@language') ? RDF.langString : RDF::XSD.string
|
67
67
|
if datatype == RDF::URI(RDF.to_uri + "JSON")
|
68
68
|
value = value.to_json_c14n
|
69
69
|
end
|
data/lib/json/ld/utils.rb
CHANGED
@@ -11,8 +11,8 @@ module JSON::LD
|
|
11
11
|
# @return [Boolean]
|
12
12
|
def node?(value)
|
13
13
|
value.is_a?(Hash) &&
|
14
|
-
!(value.
|
15
|
-
(value.length > 1 || !value.
|
14
|
+
!(value.key?('@value') || value.key?('@list') || value.key?('@set')) &&
|
15
|
+
(value.length > 1 || !value.key?('@id'))
|
16
16
|
end
|
17
17
|
|
18
18
|
##
|
@@ -29,7 +29,7 @@ module JSON::LD
|
|
29
29
|
# @return [Boolean]
|
30
30
|
def node_or_ref?(value)
|
31
31
|
value.is_a?(Hash) &&
|
32
|
-
!(value.
|
32
|
+
!(value.key?('@value') || value.key?('@list') || value.key?('@set'))
|
33
33
|
end
|
34
34
|
|
35
35
|
##
|
@@ -66,7 +66,7 @@ module JSON::LD
|
|
66
66
|
# @param [Object] value
|
67
67
|
# @return [Boolean]
|
68
68
|
def simple_graph?(value)
|
69
|
-
graph?(value) && !value.
|
69
|
+
graph?(value) && !value.key?('@id')
|
70
70
|
end
|
71
71
|
|
72
72
|
##
|
@@ -75,7 +75,7 @@ module JSON::LD
|
|
75
75
|
# @param [Object] value
|
76
76
|
# @return [Boolean]
|
77
77
|
def list?(value)
|
78
|
-
value.is_a?(Hash) && value.
|
78
|
+
value.is_a?(Hash) && value.key?('@list')
|
79
79
|
end
|
80
80
|
|
81
81
|
##
|
@@ -84,7 +84,7 @@ module JSON::LD
|
|
84
84
|
# @param [Object] value
|
85
85
|
# @return [Boolean]
|
86
86
|
def index?(value)
|
87
|
-
value.is_a?(Hash) && value.
|
87
|
+
value.is_a?(Hash) && value.key?('@index')
|
88
88
|
end
|
89
89
|
|
90
90
|
##
|
@@ -93,7 +93,7 @@ module JSON::LD
|
|
93
93
|
# @param [Object] value
|
94
94
|
# @return [Boolean]
|
95
95
|
def value?(value)
|
96
|
-
value.is_a?(Hash) && value.
|
96
|
+
value.is_a?(Hash) && value.key?('@value')
|
97
97
|
end
|
98
98
|
|
99
99
|
##
|
@@ -170,7 +170,7 @@ module JSON::LD
|
|
170
170
|
end
|
171
171
|
elsif subject[property]
|
172
172
|
# check if subject already has value if duplicates not allowed
|
173
|
-
_has_value = !allow_duplicate && has_value(subject, property, value)
|
173
|
+
_has_value = !allow_duplicate && has_value?(subject, property, value)
|
174
174
|
|
175
175
|
# make property an array if value not present or always an array
|
176
176
|
if !subject[property].is_a?(Array) && (!_has_value || property_is_array)
|
@@ -188,7 +188,7 @@ module JSON::LD
|
|
188
188
|
# @param property the property to look for.
|
189
189
|
#
|
190
190
|
# @return [Boolean] true if the subject has the given property, false if not.
|
191
|
-
def
|
191
|
+
def property?(subject, property)
|
192
192
|
return false unless value = subject[property]
|
193
193
|
!value.is_a?(Array) || !value.empty?
|
194
194
|
end
|
@@ -200,8 +200,8 @@ module JSON::LD
|
|
200
200
|
# @param [Object] value the value to check.
|
201
201
|
#
|
202
202
|
# @return [Boolean] true if the value exists, false if not.
|
203
|
-
def has_value(subject, property, value)
|
204
|
-
if
|
203
|
+
def has_value?(subject, property, value)
|
204
|
+
if property?(subject, property)
|
205
205
|
val = subject[property]
|
206
206
|
is_list = list?(val)
|
207
207
|
if val.is_a?(Array) || is_list
|
@@ -265,7 +265,7 @@ module JSON::LD
|
|
265
265
|
# @return [String]
|
266
266
|
def get_sym(old = "")
|
267
267
|
old = old.to_s.sub(/_:/, '')
|
268
|
-
if old && self.
|
268
|
+
if old && self.key?(old)
|
269
269
|
self[old]
|
270
270
|
elsif !old.empty?
|
271
271
|
self[old] = RDF::Node.new.to_unique_base[2..-1]
|
@@ -289,7 +289,7 @@ module JSON::LD
|
|
289
289
|
# @return [String]
|
290
290
|
def get_sym(old = "")
|
291
291
|
old = old.to_s.sub(/_:/, '')
|
292
|
-
if !old.empty? && self.
|
292
|
+
if !old.empty? && self.key?(old)
|
293
293
|
self[old]
|
294
294
|
elsif !old.empty?
|
295
295
|
@num += 1
|
data/lib/json/ld/writer.rb
CHANGED
@@ -237,8 +237,8 @@ module JSON::LD
|
|
237
237
|
# @yield [writer]
|
238
238
|
# @yieldparam [RDF::Writer] writer
|
239
239
|
def initialize(output = $stdout, **options, &block)
|
240
|
-
options[:base_uri] ||= options[:base] if options.
|
241
|
-
options[:base] ||= options[:base_uri] if options.
|
240
|
+
options[:base_uri] ||= options[:base] if options.key?(:base)
|
241
|
+
options[:base] ||= options[:base_uri] if options.key?(:base_uri)
|
242
242
|
super do
|
243
243
|
@repo = RDF::Repository.new
|
244
244
|
|
data/lib/json/ld.rb
CHANGED
@@ -47,6 +47,7 @@ module JSON
|
|
47
47
|
DEFAULT_CONTEXT = "http://schema.org"
|
48
48
|
|
49
49
|
KEYWORDS = Set.new(%w(
|
50
|
+
@annotation
|
50
51
|
@base
|
51
52
|
@container
|
52
53
|
@context
|
@@ -116,6 +117,7 @@ module JSON
|
|
116
117
|
class CollidingKeywords < JsonLdError; @code = "colliding keywords"; end
|
117
118
|
class ConflictingIndexes < JsonLdError; @code = "conflicting indexes"; end
|
118
119
|
class CyclicIRIMapping < JsonLdError; @code = "cyclic IRI mapping"; end
|
120
|
+
class InvalidAnnotation < JsonLdError; @code = "invalid annotation"; end
|
119
121
|
class InvalidBaseIRI < JsonLdError; @code = "invalid base IRI"; end
|
120
122
|
class InvalidContainerMapping < JsonLdError; @code = "invalid container mapping"; end
|
121
123
|
class InvalidContextEntry < JsonLdError; @code = "invalid context entry"; end
|
@@ -137,7 +139,7 @@ module JSON
|
|
137
139
|
class InvalidNestValue < JsonLdError; @code = "invalid @nest value"; end
|
138
140
|
class InvalidPrefixValue < JsonLdError; @code = "invalid @prefix value"; end
|
139
141
|
class InvalidPropagateValue < JsonLdError; @code = "invalid @propagate value"; end
|
140
|
-
class InvalidEmbeddedNode < JsonLdError; @code = "invalid
|
142
|
+
class InvalidEmbeddedNode < JsonLdError; @code = "invalid embedded node"; end
|
141
143
|
class InvalidRemoteContext < JsonLdError; @code = "invalid remote context"; end
|
142
144
|
class InvalidReverseProperty < JsonLdError; @code = "invalid reverse property"; end
|
143
145
|
class InvalidReversePropertyMap < JsonLdError; @code = "invalid reverse property map"; end
|
data/spec/api_spec.rb
CHANGED
@@ -44,7 +44,7 @@ describe JSON::LD::API do
|
|
44
44
|
end
|
45
45
|
|
46
46
|
context "Test Files" do
|
47
|
-
%
|
47
|
+
%i(oj json_gem ok_json yajl).each do |adapter|
|
48
48
|
context "with MultiJson adapter #{adapter.inspect}" do
|
49
49
|
Dir.glob(File.expand_path(File.join(File.dirname(__FILE__), 'test-files/*-input.*'))) do |filename|
|
50
50
|
test = File.basename(filename).sub(/-input\..*$/, '')
|