json-ld 0.1.0 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/History.markdown +15 -0
- data/README.markdown +199 -3
- data/VERSION +1 -1
- data/lib/json/ld.rb +44 -4
- data/lib/json/ld/api.rb +220 -224
- data/lib/json/ld/compact.rb +126 -0
- data/lib/json/ld/evaluation_context.rb +428 -204
- data/lib/json/ld/expand.rb +185 -0
- data/lib/json/ld/extensions.rb +34 -7
- data/lib/json/ld/format.rb +2 -17
- data/lib/json/ld/frame.rb +452 -0
- data/lib/json/ld/from_rdf.rb +166 -0
- data/lib/json/ld/reader.rb +7 -231
- data/lib/json/ld/to_rdf.rb +181 -0
- data/lib/json/ld/utils.rb +97 -0
- data/lib/json/ld/writer.rb +33 -471
- metadata +51 -34
- data/lib/json/ld/normalize.rb +0 -120
@@ -0,0 +1,166 @@
|
|
1
|
+
require 'rdf/nquads'
|
2
|
+
|
3
|
+
module JSON::LD
|
4
|
+
module FromTriples
|
5
|
+
include Utils
|
6
|
+
|
7
|
+
##
|
8
|
+
# Generate a JSON-LD array representation from an array of `RDF::Statement`.
|
9
|
+
# Representation is in expanded form
|
10
|
+
#
|
11
|
+
# @param [Array<RDF::Statement>] input
|
12
|
+
# @param [BlankNodeNamer] namer
|
13
|
+
# @return [Array<Hash>] the JSON-LD document in normalized form
|
14
|
+
def from_statements(input, namer)
|
15
|
+
array = []
|
16
|
+
listMap = {}
|
17
|
+
restMap = {}
|
18
|
+
subjectMap = {}
|
19
|
+
bnode_map = {}
|
20
|
+
|
21
|
+
value = nil
|
22
|
+
ec = EvaluationContext.new
|
23
|
+
|
24
|
+
# Create a map for subject to object representation
|
25
|
+
|
26
|
+
# For each triple in input
|
27
|
+
input.each do |statement|
|
28
|
+
debug("statement") { statement.to_nquads.chomp}
|
29
|
+
|
30
|
+
subject = ec.expand_iri(statement.subject).to_s
|
31
|
+
name = ec.expand_iri(statement.context).to_s if statement.context
|
32
|
+
subject = namer.get_name(subject) if subject[0,2] == "_:"
|
33
|
+
name = namer.get_name(name) if name.to_s[0,2] == "_:"
|
34
|
+
|
35
|
+
case statement.predicate
|
36
|
+
when RDF.first
|
37
|
+
# If property is rdf:first,
|
38
|
+
# create a new entry in _listMap_ for _name_ and _subject_ and an array value
|
39
|
+
# containing the object representation and continue to the next statement.
|
40
|
+
object_rep = ec.expand_value(nil, statement.object)
|
41
|
+
object_rep['@id'] = namer.get_name(object_rep['@id']) if blank_node?(object_rep)
|
42
|
+
debug("rdf:first") { "save object #{[object_rep].inspect}"}
|
43
|
+
listMap[name] ||= {}
|
44
|
+
listMap[name][subject] = [object_rep]
|
45
|
+
next
|
46
|
+
when RDF.rest
|
47
|
+
# If property is rdf:rest,
|
48
|
+
# and object is a blank node,
|
49
|
+
# create a new entry in _restMap_ for _name_ and _subject_ and a value being the
|
50
|
+
# result of IRI expansion on the object and continue to the next statement.
|
51
|
+
next unless statement.object.is_a?(RDF::Node)
|
52
|
+
object_rep = ec.expand_iri(statement.object).to_s
|
53
|
+
object_rep = namer.get_name(object_rep) if object_rep[0,2] == '_:'
|
54
|
+
debug("rdf:rest") { "save object #{object_rep.inspect}"}
|
55
|
+
restMap[name] ||= {}
|
56
|
+
restMap[name][subject] = object_rep
|
57
|
+
next
|
58
|
+
end
|
59
|
+
|
60
|
+
# If name is not null
|
61
|
+
if name
|
62
|
+
# If _subjectMap_ does not have an entry for null as name and _name_ as subject
|
63
|
+
subjectMap[nil] ||= {}
|
64
|
+
value = subjectMap[nil][name]
|
65
|
+
unless value
|
66
|
+
# Create a new JSON Object with key/value pair of @id and a string representation
|
67
|
+
# of name and append to array.
|
68
|
+
debug("@id") { "new subject: #{name} for graph"}
|
69
|
+
value = Hash.ordered
|
70
|
+
value['@id'] = name
|
71
|
+
array << (subjectMap[nil][name] = value)
|
72
|
+
else
|
73
|
+
# Otherwise, use that entry as value
|
74
|
+
end
|
75
|
+
|
76
|
+
# If value does not have an entry for @graph, initialize it as a new array
|
77
|
+
a = value['@graph'] ||= []
|
78
|
+
|
79
|
+
# If subjectMap does not have an entry for name and subject
|
80
|
+
subjectMap[name] ||= {}
|
81
|
+
value = subjectMap[name][subject]
|
82
|
+
unless value
|
83
|
+
# Create a new JSON Object with key/value pair of @id and a string representation
|
84
|
+
# of name and append to the the graph array for name and use as value.
|
85
|
+
debug("@id") { "new subject: #{subject} for graph: #{name}"}
|
86
|
+
value = Hash.ordered
|
87
|
+
value['@id'] = subject
|
88
|
+
a << (subjectMap[name][subject] = value)
|
89
|
+
else
|
90
|
+
# Otherwise, use that entry as value
|
91
|
+
end
|
92
|
+
else
|
93
|
+
# Otherwise, if subjectMap does not have an entry for _name_ and _subject_
|
94
|
+
subjectMap[name] ||= {}
|
95
|
+
value = subjectMap[nil][subject]
|
96
|
+
unless value
|
97
|
+
# Create a new JSON Object with key/value pair of @id and a string representation
|
98
|
+
# of subject and append to array.
|
99
|
+
debug("@id") { "new subject: #{subject}"}
|
100
|
+
value = Hash.ordered
|
101
|
+
value['@id'] = subject
|
102
|
+
array << (subjectMap[nil][subject] = value)
|
103
|
+
else
|
104
|
+
# Otherwise, use that entry as value
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# If property is http://www.w3.org/1999/02/22-rdf-syntax-ns#type:
|
109
|
+
if statement.predicate == RDF.type
|
110
|
+
object = ec.expand_iri(statement.object).to_s
|
111
|
+
debug("@type") { object.inspect}
|
112
|
+
# append the string representation of object to the array value for the key @type, creating
|
113
|
+
# an entry if necessary
|
114
|
+
(value['@type'] ||= []) << object
|
115
|
+
elsif statement.object == RDF.nil
|
116
|
+
# Otherwise, if object is http://www.w3.org/1999/02/22-rdf-syntax-ns#nil, let
|
117
|
+
# key be the string representation of predicate. Set the value
|
118
|
+
# for key to an empty @list representation {"@list": []}
|
119
|
+
key = ec.expand_iri(statement.predicate).to_s
|
120
|
+
value[key] = {"@list" => []}
|
121
|
+
else
|
122
|
+
# Otherwise, let key be the string representation of predicate and let object representation
|
123
|
+
# be object represented in expanded form as described in Value Expansion.
|
124
|
+
key = ec.expand_iri(statement.predicate).to_s
|
125
|
+
object = ec.expand_value(key, statement.object)
|
126
|
+
debug("object") {"detected that #{object.inspect} is a blank node"}
|
127
|
+
object['@id'] = object_iri = namer.get_name(object['@id']) if blank_node?(object)
|
128
|
+
|
129
|
+
debug("key/value") { "key: #{key}, :value #{object.inspect}"}
|
130
|
+
|
131
|
+
# Non-normative, save a reference for the bnode to allow for easier list expansion
|
132
|
+
bnode_map[object_iri] = {:obj => value, :key => key} if statement.object.is_a?(RDF::Node)
|
133
|
+
|
134
|
+
# append the object object representation to the array value for key, creating
|
135
|
+
# an entry if necessary
|
136
|
+
(value[key] ||= []) << object
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# For each key/value _prev_, _rest_ entry in _restMap_, append to the _listMap_ value identified
|
141
|
+
# by _prev_ the _listMap_ value identified by _rest_
|
142
|
+
debug("restMap") {restMap.inspect}
|
143
|
+
restMap.each do |gname, map|
|
144
|
+
map.each do |prev, rest|
|
145
|
+
debug("@list") { "Fold #{rest} into #{prev}"}
|
146
|
+
listMap[gname][prev] += listMap[gname][rest]
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
# For each key/value _node_, _list_, in _listMap_ where _list_ exists as a value of an object in _array_,
|
151
|
+
# replace the object value with _list_
|
152
|
+
debug("listMap") {listMap.inspect}
|
153
|
+
listMap.each do |gname, map|
|
154
|
+
map.each do |node, list|
|
155
|
+
next unless bnode_map.has_key?(node)
|
156
|
+
debug("@list") { "Replace #{bnode_map[node][:obj][bnode_map[node][:key]]} with #{listMap[node]}"}
|
157
|
+
bnode_map[node][:obj][bnode_map[node][:key]] = {"@list" => listMap[gname][node]}
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
# Return array as the graph representation.
|
162
|
+
debug("fromRdf") {array.to_json(JSON_STATE)}
|
163
|
+
array
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
data/lib/json/ld/reader.rb
CHANGED
@@ -10,10 +10,10 @@ module JSON::LD
|
|
10
10
|
format Format
|
11
11
|
|
12
12
|
##
|
13
|
-
#
|
14
|
-
|
15
|
-
|
16
|
-
|
13
|
+
# Override normal symbol generation
|
14
|
+
def self.to_sym
|
15
|
+
:jsonld
|
16
|
+
end
|
17
17
|
|
18
18
|
##
|
19
19
|
# Initializes the RDF/JSON reader instance.
|
@@ -26,6 +26,8 @@ module JSON::LD
|
|
26
26
|
# @yieldreturn [void] ignored
|
27
27
|
# @raise [RDF::ReaderError] if the JSON document cannot be loaded
|
28
28
|
def initialize(input = $stdin, options = {}, &block)
|
29
|
+
options[:base_uri] ||= options[:base] if options.has_key?(:base)
|
30
|
+
options[:base] ||= options[:base_uri] if options.has_key?(:base_uri)
|
29
31
|
super do
|
30
32
|
begin
|
31
33
|
@doc = JSON.load(input)
|
@@ -47,12 +49,7 @@ module JSON::LD
|
|
47
49
|
# @private
|
48
50
|
# @see RDF::Reader#each_statement
|
49
51
|
def each_statement(&block)
|
50
|
-
@
|
51
|
-
|
52
|
-
# initialize the evaluation context with initial context
|
53
|
-
ec = EvaluationContext.new(@options)
|
54
|
-
|
55
|
-
traverse("", @doc, nil, nil, ec)
|
52
|
+
JSON::LD::API.toRDF(@doc, @options[:context], nil, @options, &block)
|
56
53
|
end
|
57
54
|
|
58
55
|
##
|
@@ -63,227 +60,6 @@ module JSON::LD
|
|
63
60
|
block.call(*statement.to_triple)
|
64
61
|
end
|
65
62
|
end
|
66
|
-
|
67
|
-
private
|
68
|
-
##
|
69
|
-
#
|
70
|
-
# @param [String] path
|
71
|
-
# location within JSON hash
|
72
|
-
# @param [Hash, Array, String] element
|
73
|
-
# The current JSON element being processed
|
74
|
-
# @param [RDF::URI] subject
|
75
|
-
# Inherited subject
|
76
|
-
# @param [RDF::URI] property
|
77
|
-
# Inherited property
|
78
|
-
# @param [EvaluationContext] ec
|
79
|
-
# The active context
|
80
|
-
# @return [RDF::Resource] defined by this element
|
81
|
-
# @yield :resource
|
82
|
-
# @yieldparam [RDF::Resource] :resource
|
83
|
-
def traverse(path, element, subject, property, ec)
|
84
|
-
debug(path) {"traverse: e=#{element.class.inspect}, s=#{subject.inspect}, p=#{property.inspect}, e=#{ec.inspect}"}
|
85
|
-
|
86
|
-
traverse_result = case element
|
87
|
-
when Hash
|
88
|
-
# 2.1) If a @context keyword is found, the processor merges each key-value pair in
|
89
|
-
# the local context into the active context ...
|
90
|
-
if element['@context']
|
91
|
-
# Merge context
|
92
|
-
ec = ec.parse(element['@context'])
|
93
|
-
prefixes.merge!(ec.mappings) # Update parsed prefixes
|
94
|
-
end
|
95
|
-
|
96
|
-
# 2.2) Create a copy of the current JSON object, changing keys that map to JSON-LD keywords with those keywords.
|
97
|
-
# Use the new JSON object in subsequent steps
|
98
|
-
new_element = {}
|
99
|
-
element.each do |k, v|
|
100
|
-
k = ec.mapping(k) if ec.mapping(k).to_s[0,1] == '@'
|
101
|
-
new_element[k] = v
|
102
|
-
end
|
103
|
-
unless element == new_element
|
104
|
-
debug(path) {"traverse: keys after map: #{new_element.keys.inspect}"}
|
105
|
-
element = new_element
|
106
|
-
end
|
107
|
-
|
108
|
-
# Other shortcuts to allow use of this method for terminal associative arrays
|
109
|
-
object = if element['@literal']
|
110
|
-
# 2.3) If the JSON object has a @literal key, set the active object to a literal value as follows ...
|
111
|
-
literal_opts = {}
|
112
|
-
literal_opts[:datatype] = ec.expand_iri(element['@type'], :position => :datatype) if element['@type']
|
113
|
-
literal_opts[:language] = element['@language'].to_sym if element['@language']
|
114
|
-
RDF::Literal.new(element['@literal'], literal_opts)
|
115
|
-
elsif element['@list']
|
116
|
-
# 2.4 (Lists)
|
117
|
-
parse_list("#{path}[#{'@list'}]", element['@list'], property, ec) do |resource|
|
118
|
-
add_triple(path, subject, property, resource) if subject && property
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
122
|
-
if object
|
123
|
-
yield object if block_given?
|
124
|
-
return object
|
125
|
-
end
|
126
|
-
|
127
|
-
active_subject = if element['@id'].is_a?(String)
|
128
|
-
# 2.5 Subject
|
129
|
-
# 2.5.1 Set active object (subject)
|
130
|
-
ec.expand_iri(element['@id'], :position => :subject)
|
131
|
-
elsif element['@id']
|
132
|
-
# 2.5.2 Recursively process hash or Array values
|
133
|
-
traverse("#{path}[#{'@id'}]", element['@id'], subject, property, ec) do |resource|
|
134
|
-
add_triple(path, subject, property, resource) if subject && property
|
135
|
-
end
|
136
|
-
else
|
137
|
-
# 2.6) Generate a blank node identifier and set it as the active subject.
|
138
|
-
RDF::Node.new
|
139
|
-
end
|
140
|
-
|
141
|
-
subject = active_subject
|
142
|
-
|
143
|
-
# 2.7) For each key in the JSON object that has not already been processed, perform the following steps:
|
144
|
-
element.each do |key, value|
|
145
|
-
# 2.7.1) If a key that is not @context, @id, or @type, set the active property by
|
146
|
-
# performing Property Processing on the key.
|
147
|
-
property = case key
|
148
|
-
when '@type' then RDF.type
|
149
|
-
when /^@/ then next
|
150
|
-
else ec.expand_iri(key, :position => :predicate)
|
151
|
-
end
|
152
|
-
|
153
|
-
# 2.7.3) List expansion
|
154
|
-
object = if ec.list(property) && value.is_a?(Array)
|
155
|
-
# If the active property is the target of a @list coercion, and the value is an array,
|
156
|
-
# process the value as a list starting at Step 3.1.
|
157
|
-
parse_list("#{path}[#{key}]", value, property, ec) do |resource|
|
158
|
-
# Adds triple for head BNode only, the rest of the list is done within the method
|
159
|
-
add_triple(path, subject, property, resource) if subject && property
|
160
|
-
end
|
161
|
-
else
|
162
|
-
traverse("#{path}[#{key}]", value, subject, property, ec) do |resource|
|
163
|
-
# Adds triples for each value
|
164
|
-
add_triple(path, subject, property, resource) if subject && property
|
165
|
-
end
|
166
|
-
end
|
167
|
-
end
|
168
|
-
|
169
|
-
# 2.8) The subject is returned
|
170
|
-
subject
|
171
|
-
when Array
|
172
|
-
# 3) If a regular array is detected ...
|
173
|
-
element.each_with_index do |v, i|
|
174
|
-
traverse("#{path}[#{i}]", v, subject, property, ec) do |resource|
|
175
|
-
add_triple(path, subject, property, resource) if subject && property
|
176
|
-
end
|
177
|
-
end
|
178
|
-
nil # No real value returned from an array
|
179
|
-
when String
|
180
|
-
# 4) Perform coersion of the value, or generate a literal
|
181
|
-
debug(path) do
|
182
|
-
"traverse(#{element}): coerce(#{property.inspect}) == #{ec.coerce(property).inspect}, " +
|
183
|
-
"ec=#{ec.coercions.inspect}"
|
184
|
-
end
|
185
|
-
if ec.coerce(property) == '@id'
|
186
|
-
# 4.1) If the active property is the target of a @id coercion ...
|
187
|
-
ec.expand_iri(element, :position => :object)
|
188
|
-
elsif ec.coerce(property)
|
189
|
-
# 4.2) Otherwise, if the active property is the target of coercion ..
|
190
|
-
RDF::Literal.new(element, :datatype => ec.coerce(property))
|
191
|
-
else
|
192
|
-
# 4.3) Otherwise, set the active object to a plain literal value created from the string.
|
193
|
-
RDF::Literal.new(element, :language => ec.language)
|
194
|
-
end
|
195
|
-
when Float
|
196
|
-
object = RDF::Literal::Double.new(element)
|
197
|
-
debug(path) {"traverse(#{element}): native: #{object.inspect}"}
|
198
|
-
object
|
199
|
-
when Fixnum
|
200
|
-
object = RDF::Literal.new(element)
|
201
|
-
debug(path) {"traverse(#{element}): native: #{object.inspect}"}
|
202
|
-
object
|
203
|
-
when TrueClass, FalseClass
|
204
|
-
object = RDF::Literal::Boolean.new(element)
|
205
|
-
debug(path) {"traverse(#{element}): native: #{object.inspect}"}
|
206
|
-
object
|
207
|
-
else
|
208
|
-
raise RDF::ReaderError, "Traverse to unknown element: #{element.inspect} of type #{element.class}"
|
209
|
-
end
|
210
|
-
|
211
|
-
# Yield and return traverse_result
|
212
|
-
yield traverse_result if traverse_result && block_given?
|
213
|
-
traverse_result
|
214
|
-
end
|
215
|
-
|
216
|
-
##
|
217
|
-
# Parse a List
|
218
|
-
#
|
219
|
-
# @param [String] path
|
220
|
-
# location within JSON hash
|
221
|
-
# @param [Array] list
|
222
|
-
# The Array to serialize as a list
|
223
|
-
# @param [RDF::URI] property
|
224
|
-
# Inherited property
|
225
|
-
# @param [EvaluationContext] ec
|
226
|
-
# The active context
|
227
|
-
# @return [RDF::Resource] BNode or nil for head of list
|
228
|
-
# @yield :resource
|
229
|
-
# BNode or nil for head of list
|
230
|
-
# @yieldparam [RDF::Resource] :resource
|
231
|
-
def parse_list(path, list, property, ec)
|
232
|
-
debug(path) {"list: #{list.inspect}, p=#{property.inspect}, e=#{ec.inspect}"}
|
233
|
-
|
234
|
-
last = list.pop
|
235
|
-
result = first_bnode = last ? RDF::Node.new : RDF.nil
|
236
|
-
|
237
|
-
list.each do |list_item|
|
238
|
-
# Traverse the value, using _property_, not rdf:first, to ensure that
|
239
|
-
# proper type coercion is performed
|
240
|
-
traverse("#{path}", list_item, first_bnode, property, ec) do |resource|
|
241
|
-
add_triple("#{path}", first_bnode, RDF.first, resource)
|
242
|
-
end
|
243
|
-
rest_bnode = RDF::Node.new
|
244
|
-
add_triple("#{path}", first_bnode, RDF.rest, rest_bnode)
|
245
|
-
first_bnode = rest_bnode
|
246
|
-
end
|
247
|
-
if last
|
248
|
-
traverse("#{path}", last, first_bnode, property, ec) do |resource|
|
249
|
-
add_triple("#{path}", first_bnode, RDF.first, resource)
|
250
|
-
end
|
251
|
-
add_triple("#{path}", first_bnode, RDF.rest, RDF.nil)
|
252
|
-
end
|
253
|
-
|
254
|
-
yield result if block_given?
|
255
|
-
result
|
256
|
-
end
|
257
|
-
|
258
|
-
##
|
259
|
-
# add a statement, object can be literal or URI or bnode
|
260
|
-
#
|
261
|
-
# @param [String] path
|
262
|
-
# @param [URI, BNode] subject the subject of the statement
|
263
|
-
# @param [URI] predicate the predicate of the statement
|
264
|
-
# @param [URI, BNode, Literal] object the object of the statement
|
265
|
-
# @return [Statement] Added statement
|
266
|
-
# @raise [ReaderError] Checks parameter types and raises if they are incorrect if parsing mode is _validate_.
|
267
|
-
def add_triple(path, subject, predicate, object)
|
268
|
-
predicate = RDF.type if predicate == '@type'
|
269
|
-
statement = RDF::Statement.new(subject, predicate, object)
|
270
|
-
debug(path) {"statement: #{statement.to_ntriples}"}
|
271
|
-
@callback.call(statement)
|
272
|
-
end
|
273
|
-
|
274
|
-
##
|
275
|
-
# Add debug event to debug array, if specified
|
276
|
-
#
|
277
|
-
# @param [XML Node, any] node:: XML Node or string for showing context
|
278
|
-
# @param [String] message
|
279
|
-
# @yieldreturn [String] appended to message, to allow for lazy-evaulation of message
|
280
|
-
def debug(*args)
|
281
|
-
return unless ::JSON::LD.debug? || @options[:debug]
|
282
|
-
message = " " * (@depth || 0) * 2 + (args.empty? ? "" : args.join(": "))
|
283
|
-
message += yield if block_given?
|
284
|
-
puts message if JSON::LD::debug?
|
285
|
-
@options[:debug] << message if @options[:debug].is_a?(Array)
|
286
|
-
end
|
287
63
|
end
|
288
64
|
end
|
289
65
|
|
@@ -0,0 +1,181 @@
|
|
1
|
+
require 'rdf/nquads'
|
2
|
+
|
3
|
+
module JSON::LD
|
4
|
+
module Triples
|
5
|
+
include Utils
|
6
|
+
|
7
|
+
##
|
8
|
+
#
|
9
|
+
# @param [String] path
|
10
|
+
# location within JSON hash
|
11
|
+
# @param [Hash, Array, String] element
|
12
|
+
# The current JSON element being processed
|
13
|
+
# @param [RDF::Node] subject
|
14
|
+
# Inherited subject
|
15
|
+
# @param [RDF::URI] property
|
16
|
+
# Inherited property
|
17
|
+
# @param [RDF::Node] name
|
18
|
+
# Inherited inherited graph name
|
19
|
+
# @return [RDF::Resource] defined by this element
|
20
|
+
# @yield :statement
|
21
|
+
# @yieldparam [RDF::Statement] :statement
|
22
|
+
def statements(path, element, subject, property, name, &block)
|
23
|
+
debug(path) {"statements: e=#{element.inspect}, s=#{subject.inspect}, p=#{property.inspect}, n=#{name.inspect}"}
|
24
|
+
@node_seq = "jld_t0000" unless subject || property
|
25
|
+
|
26
|
+
traverse_result = depth do
|
27
|
+
case element
|
28
|
+
when Hash
|
29
|
+
# Other shortcuts to allow use of this method for terminal associative arrays
|
30
|
+
object = if element.has_key?('@value')
|
31
|
+
# 1.2) If the JSON object has a @value key, set the active object to a literal value as follows ...
|
32
|
+
literal_opts = {}
|
33
|
+
literal_opts[:datatype] = RDF::URI(element['@type']) if element['@type']
|
34
|
+
literal_opts[:language] = element['@language'].to_sym if element['@language']
|
35
|
+
RDF::Literal.new(element['@value'], literal_opts)
|
36
|
+
elsif element.has_key?('@list')
|
37
|
+
# 1.3 (Lists)
|
38
|
+
parse_list("#{path}[#{'@list'}]", element['@list'], property, name, &block)
|
39
|
+
end
|
40
|
+
|
41
|
+
if object
|
42
|
+
# 1.4
|
43
|
+
add_quad(path, subject, property, object, name, &block) if subject && property
|
44
|
+
return object
|
45
|
+
end
|
46
|
+
|
47
|
+
active_subject = if element.fetch('@id', nil).is_a?(String)
|
48
|
+
# 1.5 Subject
|
49
|
+
# 1.5.1 Set active object (subject)
|
50
|
+
context.expand_iri(element['@id'], :quite => true)
|
51
|
+
else
|
52
|
+
# 1.6) Generate a blank node identifier and set it as the active subject.
|
53
|
+
node
|
54
|
+
end
|
55
|
+
|
56
|
+
# 1.7) For each key in the JSON object that has not already been processed,
|
57
|
+
# perform the following steps:
|
58
|
+
element.each do |key, value|
|
59
|
+
active_property = case key
|
60
|
+
when '@type'
|
61
|
+
# If the key is @type, set the active property to rdf:type.
|
62
|
+
RDF.type
|
63
|
+
when '@graph'
|
64
|
+
# Otherwise, if property is @graph, process value algorithm recursively, using active subject
|
65
|
+
# as graph name and null values for active subject and active property and then continue to
|
66
|
+
# next property
|
67
|
+
statements("#{path}[#{key}]", value, nil, nil, active_subject, &block)
|
68
|
+
next
|
69
|
+
when /^@/
|
70
|
+
# Otherwise, if property is a keyword, skip this step.
|
71
|
+
next
|
72
|
+
else
|
73
|
+
# 1.7.1) If a key that is not @id, @graph, or @type, set the active property by
|
74
|
+
# performing Property Processing on the key.
|
75
|
+
context.expand_iri(key, :quite => true)
|
76
|
+
end
|
77
|
+
|
78
|
+
debug("statements[Step 1.7.4]")
|
79
|
+
statements("#{path}[#{key}]", value, active_subject, active_property, name, &block)
|
80
|
+
end
|
81
|
+
|
82
|
+
# 1.8) The active_subject is returned
|
83
|
+
active_subject
|
84
|
+
when Array
|
85
|
+
# 2) If a regular array is detected ...
|
86
|
+
debug("statements[Step 2]")
|
87
|
+
element.each_with_index do |v, i|
|
88
|
+
statements("#{path}[#{i}]", v, subject, property, name, &block)
|
89
|
+
end
|
90
|
+
nil # No real value returned from an array
|
91
|
+
when String
|
92
|
+
object = RDF::Literal.new(element)
|
93
|
+
debug(path) {"statements[Step 3]: plain: #{object.inspect}"}
|
94
|
+
object
|
95
|
+
when Float
|
96
|
+
object = RDF::Literal::Double.new(element)
|
97
|
+
debug(path) {"statements[Step 4]: native: #{object.inspect}"}
|
98
|
+
object
|
99
|
+
when Fixnum
|
100
|
+
object = RDF::Literal.new(element)
|
101
|
+
debug(path) {"statements[Step 5]: native: #{object.inspect}"}
|
102
|
+
object
|
103
|
+
when TrueClass, FalseClass
|
104
|
+
object = RDF::Literal::Boolean.new(element)
|
105
|
+
debug(path) {"statements[Step 6]: native: #{object.inspect}"}
|
106
|
+
object
|
107
|
+
else
|
108
|
+
raise RDF::ReaderError, "Traverse to unknown element: #{element.inspect} of type #{element.class}"
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# Yield and return traverse_result
|
113
|
+
add_quad(path, subject, property, traverse_result, name, &block) if subject && property && traverse_result
|
114
|
+
traverse_result
|
115
|
+
end
|
116
|
+
|
117
|
+
##
|
118
|
+
# Parse a List
|
119
|
+
#
|
120
|
+
# @param [String] path
|
121
|
+
# location within JSON hash
|
122
|
+
# @param [Array] list
|
123
|
+
# The Array to serialize as a list
|
124
|
+
# @param [RDF::URI] property
|
125
|
+
# Inherited property
|
126
|
+
# @param [RDF::Resource] name
|
127
|
+
# Inherited named graph context
|
128
|
+
# @param [EvaluationContext] ec
|
129
|
+
# The active context
|
130
|
+
# @return [RDF::Resource] BNode or nil for head of list
|
131
|
+
# @yield :statement
|
132
|
+
# @yieldparam [RDF::Statement] :statement
|
133
|
+
def parse_list(path, list, property, name, &block)
|
134
|
+
debug(path) {"list: #{list.inspect}, p=#{property.inspect}, n=#{name.inspect}"}
|
135
|
+
|
136
|
+
last = list.pop
|
137
|
+
result = first_bnode = last ? node : RDF.nil
|
138
|
+
|
139
|
+
depth do
|
140
|
+
list.each do |list_item|
|
141
|
+
# Traverse the value
|
142
|
+
statements("#{path}", list_item, first_bnode, RDF.first, name, &block)
|
143
|
+
rest_bnode = node
|
144
|
+
add_quad("#{path}", first_bnode, RDF.rest, rest_bnode, name, &block)
|
145
|
+
first_bnode = rest_bnode
|
146
|
+
end
|
147
|
+
if last
|
148
|
+
statements("#{path}", last, first_bnode, RDF.first, name, &block)
|
149
|
+
add_quad("#{path}", first_bnode, RDF.rest, RDF.nil, name, &block)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
result
|
153
|
+
end
|
154
|
+
|
155
|
+
##
|
156
|
+
# Create a new named node using the sequence
|
157
|
+
def node
|
158
|
+
n = RDF::Node.new(@node_seq)
|
159
|
+
@node_seq = @node_seq.succ
|
160
|
+
n
|
161
|
+
end
|
162
|
+
|
163
|
+
##
|
164
|
+
# add a statement, object can be literal or URI or bnode
|
165
|
+
#
|
166
|
+
# @param [String] path
|
167
|
+
# @param [RDF::Resource] subject the subject of the statement
|
168
|
+
# @param [RDF::URI] predicate the predicate of the statement
|
169
|
+
# @param [RDF::Term] object the object of the statement
|
170
|
+
# @param [RDF::Resource] name the named graph context of the statement
|
171
|
+
# @yield :statement
|
172
|
+
# @yieldparam [RDF::Statement] :statement
|
173
|
+
def add_quad(path, subject, predicate, object, name)
|
174
|
+
predicate = RDF.type if predicate == '@type'
|
175
|
+
object = RDF::URI(object.to_s) if object.literal? && predicate == RDF.type
|
176
|
+
statement = RDF::Statement.new(subject, predicate, object, :context => name)
|
177
|
+
debug(path) {"statement: #{statement.to_nquads}"}
|
178
|
+
yield statement
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|