json-ld 0.0.3 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/VERSION +1 -1
- data/lib/json/ld.rb +50 -0
- data/lib/json/ld/extensions.rb +34 -0
- data/lib/json/ld/format.rb +16 -0
- data/lib/json/ld/reader.rb +44 -62
- data/lib/json/ld/writer.rb +652 -0
- metadata +60 -4
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.4
|
data/lib/json/ld.rb
CHANGED
@@ -21,11 +21,61 @@ module JSON
|
|
21
21
|
# @author [Gregg Kellogg](http://greggkellogg.net/)
|
22
22
|
module LD
|
23
23
|
require 'json'
|
24
|
+
require 'json/ld/extensions'
|
24
25
|
require 'json/ld/format'
|
25
26
|
autoload :Reader, 'json/ld/reader'
|
26
27
|
autoload :VERSION, 'json/ld/version'
|
27
28
|
autoload :Writer, 'json/ld/writer'
|
28
29
|
|
30
|
+
# Keywords
|
31
|
+
BASE = '@base'.freeze
|
32
|
+
COERCE = '@coerce'.freeze
|
33
|
+
CONTEXT = '@context'.freeze
|
34
|
+
DATATYPE = '@datatype'.freeze
|
35
|
+
IRI = '@iri'.freeze
|
36
|
+
LANGUAGE = '@language'.freeze
|
37
|
+
LITERAL = '@literal'.freeze
|
38
|
+
SUBJECT = '@subject'.freeze
|
39
|
+
TYPE = '@type'.freeze
|
40
|
+
VOCAB = '@vocab'.freeze
|
41
|
+
|
42
|
+
# Default context
|
43
|
+
# @see http://json-ld.org/spec/ED/20110507/#the-default-context
|
44
|
+
DEFAULT_CONTEXT = {
|
45
|
+
'rdf' => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
|
46
|
+
'rdfs' => 'http://www.w3.org/2000/01/rdf-schema#',
|
47
|
+
'owl' => 'http://www.w3.org/2002/07/owl#',
|
48
|
+
'xsd' => 'http://www.w3.org/2001/XMLSchema#',
|
49
|
+
'dcterms' => 'http://purl.org/dc/terms/',
|
50
|
+
'foaf' => 'http://xmlns.com/foaf/0.1/',
|
51
|
+
'cal' => 'http://www.w3.org/2002/12/cal/ical#',
|
52
|
+
'vcard' => 'http://www.w3.org/2006/vcard/ns# ',
|
53
|
+
'geo' => 'http://www.w3.org/2003/01/geo/wgs84_pos#',
|
54
|
+
'cc' => 'http://creativecommons.org/ns#',
|
55
|
+
'sioc' => 'http://rdfs.org/sioc/ns#',
|
56
|
+
'doap' => 'http://usefulinc.com/ns/doap#',
|
57
|
+
'com' => 'http://purl.org/commerce#',
|
58
|
+
'ps' => 'http://purl.org/payswarm#',
|
59
|
+
'gr' => 'http://purl.org/goodrelations/v1#',
|
60
|
+
'sig' => 'http://purl.org/signature#',
|
61
|
+
'ccard' => 'http://purl.org/commerce/creditcard#',
|
62
|
+
'@coerce' => {
|
63
|
+
# Note: rdf:type is not in the document, but necessary for this implementation
|
64
|
+
'xsd:anyURI' => ['rdf:type', 'rdf:rest', 'foaf:homepage', 'foaf:member'],
|
65
|
+
'xsd:integer' => 'foaf:age',
|
66
|
+
}
|
67
|
+
}.freeze
|
68
|
+
|
69
|
+
# Default type coercion, in property => datatype order
|
70
|
+
DEFAULT_COERCE = {
|
71
|
+
RDF.type => RDF::XSD.anyURI,
|
72
|
+
RDF.first => false, # Make sure @coerce isn't generated for this
|
73
|
+
RDF.rest => RDF::XSD.anyURI,
|
74
|
+
RDF::FOAF.homepage => RDF::XSD.anyURI,
|
75
|
+
RDF::FOAF.member => RDF::XSD.anyURI,
|
76
|
+
RDF::FOAF.age => RDF::XSD.integer,
|
77
|
+
}.freeze
|
78
|
+
|
29
79
|
def self.debug?; @debug; end
|
30
80
|
def self.debug=(value); @debug = value; end
|
31
81
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module RDF
|
2
|
+
class Graph
|
3
|
+
# Resource properties
|
4
|
+
#
|
5
|
+
# Properties arranged as a hash with the predicate Term as index to an array of resources or literals
|
6
|
+
#
|
7
|
+
# Example:
|
8
|
+
# graph.load(':foo a :bar; rdfs:label "An example" .', "http://example.com/")
|
9
|
+
# graph.resources(URI.new("http://example.com/subject")) =>
|
10
|
+
# {
|
11
|
+
# "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" => [<http://example.com/#bar>],
|
12
|
+
# "http://example.com/#label" => ["An example"]
|
13
|
+
# }
|
14
|
+
def properties(subject, recalc = false)
|
15
|
+
@properties ||= {}
|
16
|
+
@properties.delete(subject.to_s) if recalc
|
17
|
+
@properties[subject.to_s] ||= begin
|
18
|
+
hash = Hash.new
|
19
|
+
self.query(:subject => subject) do |statement|
|
20
|
+
pred = statement.predicate.to_s
|
21
|
+
|
22
|
+
hash[pred] ||= []
|
23
|
+
hash[pred] << statement.object
|
24
|
+
end
|
25
|
+
hash
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Get type(s) of subject, returns a list of symbols
|
30
|
+
def type_of(subject)
|
31
|
+
query(:subject => subject, :predicate => RDF.type).map {|st| st.object}
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/json/ld/format.rb
CHANGED
@@ -28,4 +28,20 @@ module JSON::LD
|
|
28
28
|
reader { JSON::LD::Reader }
|
29
29
|
writer { JSON::LD::Writer }
|
30
30
|
end
|
31
|
+
|
32
|
+
# Alias for JSON-LD format
|
33
|
+
#
|
34
|
+
# This allows the following:
|
35
|
+
#
|
36
|
+
# @example Obtaining an Notation3 format class
|
37
|
+
# RDF::Format.for(:jsonld) #=> JSON::LD::JSONLD
|
38
|
+
# RDF::Format.for(:jsonld).reader #=> JSON::LD::Reader
|
39
|
+
# RDF::Format.for(:jsonld).writer #=> JSON::LD::Writer
|
40
|
+
class JSONLD < RDF::Format
|
41
|
+
content_type 'application/json', :extension => :jsonld
|
42
|
+
content_encoding 'utf-8'
|
43
|
+
|
44
|
+
reader { JSON::LD::Reader }
|
45
|
+
writer { JSON::LD::Writer }
|
46
|
+
end
|
31
47
|
end
|
data/lib/json/ld/reader.rb
CHANGED
@@ -7,33 +7,6 @@ module JSON::LD
|
|
7
7
|
class Reader < RDF::Reader
|
8
8
|
format Format
|
9
9
|
|
10
|
-
# Default context
|
11
|
-
# @see http://json-ld.org/spec/ED/20110507/#the-default-context
|
12
|
-
DEFAULT_CONTEXT = {
|
13
|
-
"rdf" => "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
|
14
|
-
"rdfs" => "http://www.w3.org/2000/01/rdf-schema#",
|
15
|
-
"owl" => "http://www.w3.org/2002/07/owl#",
|
16
|
-
"xsd" => "http://www.w3.org/2001/XMLSchema#",
|
17
|
-
"dcterms" => "http://purl.org/dc/terms/",
|
18
|
-
"foaf" => "http://xmlns.com/foaf/0.1/",
|
19
|
-
"cal" => "http://www.w3.org/2002/12/cal/ical#",
|
20
|
-
"vcard" => "http://www.w3.org/2006/vcard/ns# ",
|
21
|
-
"geo" => "http://www.w3.org/2003/01/geo/wgs84_pos#",
|
22
|
-
"cc" => "http://creativecommons.org/ns#",
|
23
|
-
"sioc" => "http://rdfs.org/sioc/ns#",
|
24
|
-
"doap" => "http://usefulinc.com/ns/doap#",
|
25
|
-
"com" => "http://purl.org/commerce#",
|
26
|
-
"ps" => "http://purl.org/payswarm#",
|
27
|
-
"gr" => "http://purl.org/goodrelations/v1#",
|
28
|
-
"sig" => "http://purl.org/signature#",
|
29
|
-
"ccard" => "http://purl.org/commerce/creditcard#",
|
30
|
-
"@coerce" => {
|
31
|
-
# Note: rdf:type is not in the document, but necessary for this implementation
|
32
|
-
"xsd:anyURI" => ["rdf:type", "foaf:homepage", "foaf:member", "rdf:type"],
|
33
|
-
"xsd:integer" => "foaf:age",
|
34
|
-
}
|
35
|
-
}.freeze
|
36
|
-
|
37
10
|
##
|
38
11
|
# The graph constructed when parsing.
|
39
12
|
#
|
@@ -181,44 +154,49 @@ module JSON::LD
|
|
181
154
|
|
182
155
|
# 2.1) If a @context keyword is found, the processor merges each key-value pair in
|
183
156
|
# the local context into the active context ...
|
184
|
-
if element[
|
157
|
+
if element[CONTEXT]
|
185
158
|
# Merge context
|
186
|
-
ec = parse_context(ec.dup, element[
|
159
|
+
ec = parse_context(ec.dup, element[CONTEXT])
|
187
160
|
prefixes.merge!(ec.mappings) # Update parsed prefixes
|
188
161
|
end
|
189
162
|
|
190
163
|
# Other shortcuts to allow use of this method for terminal associative arrays
|
191
|
-
if element[
|
164
|
+
if element[IRI].is_a?(String)
|
192
165
|
# Return the IRI found from the value
|
193
|
-
object = expand_term(element[
|
166
|
+
object = expand_term(element[IRI], ec.base, ec)
|
194
167
|
add_triple(path, subject, property, object) if subject && property
|
195
|
-
|
168
|
+
return
|
169
|
+
elsif element[LITERAL]
|
196
170
|
literal_opts = {}
|
197
|
-
literal_opts[:datatype] = expand_term(element[
|
198
|
-
literal_opts[:language] = element[
|
199
|
-
object = RDF::Literal.new(element[
|
171
|
+
literal_opts[:datatype] = expand_term(element[DATATYPE], ec.vocab.to_s, ec) if element[DATATYPE]
|
172
|
+
literal_opts[:language] = element[LANGUAGE].to_sym if element[LANGUAGE]
|
173
|
+
object = RDF::Literal.new(element[LITERAL], literal_opts)
|
200
174
|
add_triple(path, subject, property, object) if subject && property
|
175
|
+
return
|
201
176
|
end
|
202
177
|
|
203
178
|
# 2.2) ... Otherwise, if the local context is known perform the following steps:
|
204
179
|
# 2.2.1) If a @ key is found, the processor sets the active subject to the
|
205
180
|
# value after Object Processing has been performed.
|
206
|
-
if element[
|
207
|
-
active_subject = expand_term(element[
|
208
|
-
|
209
|
-
#
|
210
|
-
#
|
211
|
-
# subject, the inherited property for the property, and the active
|
212
|
-
# subject for the object.
|
213
|
-
add_triple(path, subject, property, active_subject) if subject && property
|
214
|
-
|
215
|
-
subject = active_subject
|
181
|
+
if element[SUBJECT].is_a?(String)
|
182
|
+
active_subject = expand_term(element[SUBJECT], ec.base, ec)
|
183
|
+
elsif element[SUBJECT]
|
184
|
+
# Recursively process hash or Array values
|
185
|
+
traverse("#{path}[@]", element[SUBJECT], subject, property, ec)
|
216
186
|
else
|
217
187
|
# 2.2.7) If the end of the associative array is detected, and a active subject
|
218
188
|
# was not discovered, then:
|
219
189
|
# 2.2.7.1) Generate a blank node identifier and set it as the active subject.
|
220
|
-
|
190
|
+
active_subject = RDF::Node.new
|
221
191
|
end
|
192
|
+
|
193
|
+
# 2.2.1.1) If the inherited subject and inherited property values are
|
194
|
+
# specified, generate a triple using the inherited subject for the
|
195
|
+
# subject, the inherited property for the property, and the active
|
196
|
+
# subject for the object.
|
197
|
+
# 2.2.7.2) Complete any previously incomplete triples by running all substeps of Step 2.2.1.
|
198
|
+
add_triple(path, subject, property, active_subject) if subject && property
|
199
|
+
subject = active_subject
|
222
200
|
|
223
201
|
element.each do |key, value|
|
224
202
|
# 2.2.3) If a key that is not @context, @, or a, set the active property by
|
@@ -319,23 +297,12 @@ module JSON::LD
|
|
319
297
|
# @raise [RDF::ReaderError] on a syntax error, or a reference to a term which is not defined.
|
320
298
|
def parse_context(ec, context)
|
321
299
|
context.each do |key, value|
|
322
|
-
|
300
|
+
add_debug("parse_context(#{key})", value.inspect)
|
323
301
|
case key
|
324
|
-
when
|
325
|
-
when
|
326
|
-
when
|
327
|
-
#
|
328
|
-
# overwriting duplicate values. In the case where a mapping is indicated to a list of properties
|
329
|
-
# (e.g., { "xsd:anyURI": ["foaf:homepage", "foaf:member"] }, does this overwrite a previous mapping
|
330
|
-
# of { "xsd:anyURI": "foaf:knows" }, or add to it.
|
331
|
-
add_error RDF::ReaderError, "Expected @coerce to reference an associative array" unless value.is_a?(Hash)
|
332
|
-
value.each do |type, property|
|
333
|
-
type_uri = expand_term(type, ec.vocab, ec)
|
334
|
-
[property].flatten.compact.each do |prop|
|
335
|
-
p = expand_term(prop, ec.vocab, ec)
|
336
|
-
ec.coerce[p] = type_uri
|
337
|
-
end
|
338
|
-
end
|
302
|
+
when VOCAB then ec.vocab = value
|
303
|
+
when BASE then ec.base = uri(value)
|
304
|
+
when COERCE
|
305
|
+
# Process after prefix mapping
|
339
306
|
else
|
340
307
|
# Spec confusion: The text indicates to merge each key-value pair into the active context. Is any
|
341
308
|
# processing performed on the values. For instance, could a value be a CURIE, or {"@iri": <value>}?
|
@@ -345,6 +312,21 @@ module JSON::LD
|
|
345
312
|
end
|
346
313
|
end
|
347
314
|
|
315
|
+
if context[COERCE]
|
316
|
+
# Spec confusion: doc says to merge each key-value mapping to the local context's @coerce mapping,
|
317
|
+
# overwriting duplicate values. In the case where a mapping is indicated to a list of properties
|
318
|
+
# (e.g., { "xsd:anyURI": ["foaf:homepage", "foaf:member"] }, does this overwrite a previous mapping
|
319
|
+
# of { "xsd:anyURI": "foaf:knows" }, or add to it.
|
320
|
+
add_error RDF::ReaderError, "Expected @coerce to reference an associative array" unless context[COERCE].is_a?(Hash)
|
321
|
+
context[COERCE].each do |type, property|
|
322
|
+
type_uri = expand_term(type, ec.vocab, ec)
|
323
|
+
[property].flatten.compact.each do |prop|
|
324
|
+
p = expand_term(prop, ec.vocab, ec)
|
325
|
+
ec.coerce[p] = type_uri
|
326
|
+
end
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
348
330
|
ec
|
349
331
|
end
|
350
332
|
|
data/lib/json/ld/writer.rb
CHANGED
@@ -2,10 +2,662 @@ module JSON::LD
|
|
2
2
|
##
|
3
3
|
# A JSON-LD parser in Ruby.
|
4
4
|
#
|
5
|
+
# Note that the natural interface is to write a whole graph at a time.
|
6
|
+
# Writing statements or Triples will create a graph to add them to
|
7
|
+
# and then serialize the graph.
|
8
|
+
#
|
9
|
+
# @example Obtaining a JSON-LD writer class
|
10
|
+
# RDF::Writer.for(:jsonld) #=> RDF::N3::Writer
|
11
|
+
# RDF::Writer.for("etc/test.json")
|
12
|
+
# RDF::Writer.for(:file_name => "etc/test.json")
|
13
|
+
# RDF::Writer.for(:file_extension => "json")
|
14
|
+
# RDF::Writer.for(:content_type => "application/turtle")
|
15
|
+
#
|
16
|
+
# @example Serializing RDF graph into an JSON-LD file
|
17
|
+
# JSON::LD::Writer.open("etc/test.json") do |writer|
|
18
|
+
# writer << graph
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# @example Serializing RDF statements into an JSON-LD file
|
22
|
+
# JSON::LD::Writer.open("etc/test.json") do |writer|
|
23
|
+
# graph.each_statement do |statement|
|
24
|
+
# writer << statement
|
25
|
+
# end
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# @example Serializing RDF statements into an JSON-LD string
|
29
|
+
# JSON::LD::Writer.buffer do |writer|
|
30
|
+
# graph.each_statement do |statement|
|
31
|
+
# writer << statement
|
32
|
+
# end
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# The writer will add prefix definitions, and use them for creating @context definitions, and minting CURIEs
|
36
|
+
#
|
37
|
+
# @example Creating @base, @vocab and @context prefix definitions in output
|
38
|
+
# JSON::LD::Writer.buffer(
|
39
|
+
# :base_uri => "http://example.com/",
|
40
|
+
# :vocab => "http://example.net/"
|
41
|
+
# :prefixes => {
|
42
|
+
# nil => "http://example.com/ns#",
|
43
|
+
# :foaf => "http://xmlns.com/foaf/0.1/"}
|
44
|
+
# ) do |writer|
|
45
|
+
# graph.each_statement do |statement|
|
46
|
+
# writer << statement
|
47
|
+
# end
|
48
|
+
# end
|
49
|
+
#
|
50
|
+
# Select the :canonicalize option to output JSON-LD in canonical form
|
51
|
+
#
|
5
52
|
# @see http://json-ld.org/spec/ED/20110507/
|
53
|
+
# @see http://json-ld.org/spec/ED/20110507/#the-normalization-algorithm
|
6
54
|
# @author [Gregg Kellogg](http://greggkellogg.net/)
|
7
55
|
class Writer < RDF::Writer
|
8
56
|
format Format
|
57
|
+
|
58
|
+
# @attr [Graph] Graph of statements serialized
|
59
|
+
attr :graph
|
60
|
+
# @attr [URI] Base IRI used for relativizing IRIs
|
61
|
+
attr :base_uri
|
62
|
+
# @attr [String] Vocabulary prefix used for relativizing IRIs
|
63
|
+
attr :vocab
|
64
|
+
|
65
|
+
# Type coersion to use for serialization. Defaults to DEFAULT_COERCION
|
66
|
+
#
|
67
|
+
# Maintained as a reverse mapping of `property` => `type`.
|
68
|
+
#
|
69
|
+
# @attr [Hash{RDF::URI => RDF::URI}]
|
70
|
+
attr :coerce, true
|
71
|
+
|
72
|
+
##
|
73
|
+
# Return the pre-serialized Hash before turning into JSON
|
74
|
+
#
|
75
|
+
# @return [Hash]
|
76
|
+
def self.hash(*args, &block)
|
77
|
+
hash = {}
|
78
|
+
self.new(hash, *args, &block)
|
79
|
+
hash
|
80
|
+
end
|
81
|
+
|
82
|
+
##
|
83
|
+
# Initializes the RDF-LD writer instance.
|
84
|
+
#
|
85
|
+
# @param [IO, File] output
|
86
|
+
# the output stream
|
87
|
+
# @param [Hash{Symbol => Object}] options
|
88
|
+
# any additional options
|
89
|
+
# @option options [Encoding] :encoding (Encoding::UTF_8)
|
90
|
+
# the encoding to use on the output stream (Ruby 1.9+)
|
91
|
+
# @option options [Boolean] :canonicalize (false)
|
92
|
+
# whether to canonicalize literals when serializing
|
93
|
+
# @option options [Hash] :prefixes (Hash.new)
|
94
|
+
# the prefix mappings to use (not supported by all writers)
|
95
|
+
# @option options [#to_s] :base_uri (nil)
|
96
|
+
# Base IRI used for relativizing IRIs
|
97
|
+
# @option options [#to_s] :vocab (nil)
|
98
|
+
# Vocabulary prefix used for relativizing IRIs
|
99
|
+
# @option options [Boolean] :standard_prefixes (false)
|
100
|
+
# Add standard prefixes to @prefixes, if necessary.
|
101
|
+
# @yield [writer] `self`
|
102
|
+
# @yieldparam [RDF::Writer] writer
|
103
|
+
# @yieldreturn [void]
|
104
|
+
# @yield [writer]
|
105
|
+
# @yieldparam [RDF::Writer] writer
|
106
|
+
def initialize(output = $stdout, options = {}, &block)
|
107
|
+
super do
|
108
|
+
@graph = RDF::Graph.new
|
109
|
+
@iri_to_prefix = DEFAULT_CONTEXT.dup.delete_if {|k,v| k == COERCE}.invert
|
110
|
+
@coerce = DEFAULT_COERCE.merge(options[:coerce] || {})
|
111
|
+
if block_given?
|
112
|
+
case block.arity
|
113
|
+
when 0 then instance_eval(&block)
|
114
|
+
else block.call(self)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
##
|
121
|
+
# Write whole graph
|
122
|
+
#
|
123
|
+
# @param [Graph] graph
|
124
|
+
# @return [void]
|
125
|
+
def write_graph(graph)
|
126
|
+
add_debug "Add graph #{graph.inspect}"
|
127
|
+
@graph = graph
|
128
|
+
end
|
129
|
+
|
130
|
+
##
|
131
|
+
# Addes a statement to be serialized
|
132
|
+
# @param [RDF::Statement] statement
|
133
|
+
# @return [void]
|
134
|
+
def write_statement(statement)
|
135
|
+
@graph.insert(statement)
|
136
|
+
end
|
137
|
+
|
138
|
+
##
|
139
|
+
# Addes a triple to be serialized
|
140
|
+
# @param [RDF::Resource] subject
|
141
|
+
# @param [RDF::URI] predicate
|
142
|
+
# @param [RDF::Value] object
|
143
|
+
# @return [void]
|
144
|
+
# @raise [NotImplementedError] unless implemented in subclass
|
145
|
+
# @abstract
|
146
|
+
def write_triple(subject, predicate, object)
|
147
|
+
@graph.insert(Statement.new(subject, predicate, object))
|
148
|
+
end
|
149
|
+
|
150
|
+
##
|
151
|
+
# Outputs the Serialized JSON-LD representation of all stored triples.
|
152
|
+
#
|
153
|
+
# @return [void]
|
154
|
+
# @see #write_triple
|
155
|
+
def write_epilogue
|
156
|
+
@base_uri = RDF::URI(@options[:base_uri]) if @options[:base_uri] && !@options[:canonicalize]
|
157
|
+
@vocab = @options[:vocab] unless @options[:canonicalize]
|
158
|
+
@debug = @options[:debug]
|
159
|
+
|
160
|
+
reset
|
161
|
+
|
162
|
+
add_debug "\nserialize: graph: #{@graph.size}"
|
163
|
+
|
164
|
+
preprocess
|
165
|
+
|
166
|
+
# Don't generate context for canonical output
|
167
|
+
json_hash = @options[:canonicalize] ? {} : start_document
|
168
|
+
|
169
|
+
elements = []
|
170
|
+
order_subjects.each do |subject|
|
171
|
+
unless is_done?(subject)
|
172
|
+
elements << subject(subject, json_hash)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
return if elements.empty?
|
177
|
+
|
178
|
+
if elements.length == 1 && elements.first.is_a?(Hash)
|
179
|
+
json_hash.merge!(elements.first)
|
180
|
+
else
|
181
|
+
json_hash[SUBJECT] = elements
|
182
|
+
end
|
183
|
+
|
184
|
+
if @output.is_a?(Hash)
|
185
|
+
@output.merge!(json_hash)
|
186
|
+
else
|
187
|
+
json_state = if @options[:canonicalize]
|
188
|
+
JSON::State.new(
|
189
|
+
:indent => "",
|
190
|
+
:space => "",
|
191
|
+
:space_before => "",
|
192
|
+
:object_nl => "",
|
193
|
+
:array_nl => ""
|
194
|
+
)
|
195
|
+
else
|
196
|
+
JSON::State.new(
|
197
|
+
:indent => " ",
|
198
|
+
:space => " ",
|
199
|
+
:space_before => "",
|
200
|
+
:object_nl => "\n",
|
201
|
+
:array_nl => "\n"
|
202
|
+
)
|
203
|
+
end
|
204
|
+
@output.write(json_hash.to_json(json_state))
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
##
|
209
|
+
# Returns the representation of a IRI reference.
|
210
|
+
#
|
211
|
+
# Spec confusion: should a subject URI be normalized?
|
212
|
+
#
|
213
|
+
# @param [RDF::URI] value
|
214
|
+
# @param [Hash{Symbol => Object}] options
|
215
|
+
# @option options [:subject, :predicate, :object] position
|
216
|
+
# Useful when determining how to serialize.
|
217
|
+
# @option options [RDF::URI] property
|
218
|
+
# Property for object reference, which can be used to return
|
219
|
+
# bare strings, rather than {"iri":}
|
220
|
+
# @return [Object]
|
221
|
+
def format_uri(value, options = {})
|
222
|
+
result = case options[:position]
|
223
|
+
when :subject
|
224
|
+
# attempt base_uri replacement
|
225
|
+
short = value.to_s.sub(base_uri.to_s, "")
|
226
|
+
short == value.to_s ? (get_curie(value) || value.to_s) : short
|
227
|
+
when :predicate
|
228
|
+
# attempt vocab replacement
|
229
|
+
short = TYPE if value == RDF.type
|
230
|
+
short ||= value.to_s.sub(@vocab.to_s, "")
|
231
|
+
short == value.to_s ? (get_curie(value) || value.to_s) : short
|
232
|
+
else
|
233
|
+
# Encode like a subject
|
234
|
+
iri_range?(options[:property]) ?
|
235
|
+
format_uri(value, :position => :subject) :
|
236
|
+
{:iri => format_uri(value, :position => :subject)}
|
237
|
+
end
|
238
|
+
|
239
|
+
add_debug("format_uri(#{options.inspect}, #{value.inspect}) => #{result.inspect}")
|
240
|
+
result
|
241
|
+
end
|
242
|
+
|
243
|
+
##
|
244
|
+
# @param [RDF::Node] value
|
245
|
+
# @param [Hash{Symbol => Object}] options
|
246
|
+
# @return [String]
|
247
|
+
# @raise [NotImplementedError] unless implemented in subclass
|
248
|
+
# @abstract
|
249
|
+
def format_node(value, options = {})
|
250
|
+
format_uri(value, options)
|
251
|
+
end
|
252
|
+
|
253
|
+
##
|
254
|
+
# Returns the representation of a literal.
|
255
|
+
#
|
256
|
+
# @param [RDF::Literal, String, #to_s] literal
|
257
|
+
# @param [Hash{Symbol => Object}] options
|
258
|
+
# @option options [RDF::URI] property
|
259
|
+
# Property referencing literal for type coercion
|
260
|
+
# @return [Object]
|
261
|
+
def format_literal(literal, options = {})
|
262
|
+
if options[:canonical] || @options[:canonicalize]
|
263
|
+
return {
|
264
|
+
:literal => literal.value,
|
265
|
+
:datatype => (format_uri(literal.datatype, :position => :subject) if literal.has_datatype?),
|
266
|
+
:language => (literal.language.to_s if literal.has_language?)
|
267
|
+
}.delete_if {|k,v| v.nil?}
|
268
|
+
end
|
269
|
+
|
270
|
+
case literal
|
271
|
+
when RDF::Literal::Integer, RDF::Literal::Boolean
|
272
|
+
literal.object
|
273
|
+
when RDF::Literal
|
274
|
+
if datatype_range?(options[:property]) || !(literal.has_datatype? || literal.has_language?)
|
275
|
+
# Datatype coercion where literal has the same datatype
|
276
|
+
literal.value
|
277
|
+
else
|
278
|
+
format_literal(literal, :canonical => true)
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
##
|
284
|
+
# Serialize an RDF list
|
285
|
+
# @param [RDF::URI] object
|
286
|
+
# @param [Hash{Symbol => Object}] options
|
287
|
+
# @option options [RDF::URI] property
|
288
|
+
# Property referencing literal for type coercion
|
289
|
+
# @return [Array<Array<Object>>]
|
290
|
+
def format_list(object, options = {})
|
291
|
+
predicate = options[:property]
|
292
|
+
list = []
|
293
|
+
|
294
|
+
add_debug "format_list(#{object}, #{predicate})"
|
295
|
+
|
296
|
+
@depth += 1
|
297
|
+
while object do
|
298
|
+
subject_done(object)
|
299
|
+
p = @graph.properties(object)
|
300
|
+
item = p.fetch(RDF.first.to_s, []).first
|
301
|
+
if item
|
302
|
+
add_debug "format_list serialize #{item.inspect}"
|
303
|
+
list << if predicate || item.literal?
|
304
|
+
property(predicate, item)
|
305
|
+
else
|
306
|
+
subject(item)
|
307
|
+
end
|
308
|
+
end
|
309
|
+
object = p.fetch(RDF.rest.to_s, []).first
|
310
|
+
end
|
311
|
+
@depth -= 1
|
312
|
+
|
313
|
+
# Returns
|
314
|
+
add_debug "format_list => #{[list].inspect}"
|
315
|
+
[list]
|
316
|
+
end
|
317
|
+
|
318
|
+
private
|
319
|
+
##
|
320
|
+
# Generate @context
|
321
|
+
# @return [Hash]
|
322
|
+
def start_document
|
323
|
+
ctx = {}
|
324
|
+
ctx[BASE] = base_uri.to_s if base_uri
|
325
|
+
ctx[VOCAB] = vocab.to_s if vocab
|
326
|
+
|
327
|
+
# Prefixes
|
328
|
+
prefixes.keys.sort {|a,b| a.to_s <=> b.to_s}.each do |k|
|
329
|
+
next if DEFAULT_CONTEXT.has_key?(k.to_s)
|
330
|
+
add_debug "prefix[#{k}] => #{prefixes[k]}"
|
331
|
+
ctx[k.to_s] = prefixes[k].to_s
|
332
|
+
end
|
333
|
+
|
334
|
+
# Coerce
|
335
|
+
add_debug "start_doc: coerce= #{coerce.inspect}"
|
336
|
+
unless coerce == DEFAULT_COERCE
|
337
|
+
c_h = {}
|
338
|
+
coerce.keys.sort.each do |k|
|
339
|
+
next if coerce[k] == DEFAULT_COERCE[k] ||
|
340
|
+
coerce[k] == false ||
|
341
|
+
coerce[k] == RDF::XSD.integer ||
|
342
|
+
coerce[k] == RDF::XSD.boolean
|
343
|
+
k_iri = format_uri(k, :position => :predicate)
|
344
|
+
d_iri = format_uri(coerce[k], :position => :subject)
|
345
|
+
add_debug "coerce[#{k_iri}] => #{d_iri}"
|
346
|
+
case c_h[d_iri]
|
347
|
+
when nil
|
348
|
+
c_h[d_iri] = k_iri
|
349
|
+
when Array
|
350
|
+
c_h[d_iri] << k_iri
|
351
|
+
else
|
352
|
+
c_h[d_iri] = [c_h[d_iri], k_iri]
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
356
|
+
ctx[COERCE] = c_h unless c_h.empty?
|
357
|
+
end
|
358
|
+
|
359
|
+
add_debug "start_doc: context=#{ctx.inspect}"
|
360
|
+
# Return hash with @context, or empty
|
361
|
+
ctx.empty? ? {} : {CONTEXT => ctx}
|
362
|
+
end
|
363
|
+
|
364
|
+
# Perform any preprocessing of statements required
|
365
|
+
def preprocess
|
366
|
+
# Load defined prefixes
|
367
|
+
(@options[:prefixes] || {}).each_pair do |k, v|
|
368
|
+
@iri_to_prefix[v.to_s] = k
|
369
|
+
end
|
370
|
+
@options[:prefixes] = {} # Will define actual used when matched
|
371
|
+
|
372
|
+
@graph.each {|statement| preprocess_statement(statement)}
|
373
|
+
end
|
374
|
+
|
375
|
+
# Perform any statement preprocessing required. This is used to perform reference counts and determine required
|
376
|
+
# prefixes.
|
377
|
+
# @param [Statement] statement
|
378
|
+
def preprocess_statement(statement)
|
379
|
+
add_debug "preprocess: #{statement.inspect}"
|
380
|
+
references = ref_count(statement.object) + 1
|
381
|
+
@references[statement.object] = references
|
382
|
+
@subjects[statement.subject] = true
|
383
|
+
|
384
|
+
# Pre-fetch qnames, to fill prefixes
|
385
|
+
get_curie(statement.subject)
|
386
|
+
get_curie(statement.predicate)
|
387
|
+
if statement.object.literal?
|
388
|
+
datatype_range?(statement.predicate) # To figure out coercion requirements
|
389
|
+
else
|
390
|
+
iri_range?(statement.predicate)
|
391
|
+
get_curie(statement.object)
|
392
|
+
end
|
393
|
+
|
394
|
+
@references[statement.predicate] = ref_count(statement.predicate) + 1
|
395
|
+
end
|
396
|
+
|
397
|
+
# Serialize a subject
|
398
|
+
# Option contains referencing property, if this is recursive
|
399
|
+
# @return [Hash]
|
400
|
+
def subject(subject, options = {})
|
401
|
+
defn = {}
|
402
|
+
|
403
|
+
raise RDF::WriterError, "Illegal use of subject #{subject.inspect}, not supported" unless subject.resource?
|
404
|
+
|
405
|
+
subject_done(subject)
|
406
|
+
properties = @graph.properties(subject)
|
407
|
+
add_debug "subject: #{subject.inspect}, props: #{properties.inspect}"
|
408
|
+
|
409
|
+
@graph.query(:subject => subject).each do |st|
|
410
|
+
raise RDF::WriterError, "Illegal use of predicate #{st.predicate.inspect}, not supported in RDF/XML" unless st.predicate.uri?
|
411
|
+
end
|
412
|
+
|
413
|
+
if subject.node? && ref_count(subject) > (options[:property] ? 1 : 0) && options[:canonicalize]
|
414
|
+
raise RDF::WriterError, "Can't serialize named node when normalizing"
|
415
|
+
end
|
416
|
+
|
417
|
+
# Subject may be a list
|
418
|
+
if is_valid_list?(subject)
|
419
|
+
add_debug "subject is a list"
|
420
|
+
defn[SUBJECT] = format_list(subject)
|
421
|
+
properties.delete(RDF.first.to_s)
|
422
|
+
properties.delete(RDF.rest.to_s)
|
423
|
+
|
424
|
+
# Special case, if there are no properties, then we can just serialize the list itself
|
425
|
+
return defn[SUBJECT].first if properties.empty?
|
426
|
+
elsif subject.uri? || ref_count(subject) > 1
|
427
|
+
add_debug "subject is a uri"
|
428
|
+
# Don't need to set subject if it's a Node without references
|
429
|
+
defn[SUBJECT] = format_uri(subject, :position => :subject)
|
430
|
+
else
|
431
|
+
add_debug "subject is an unreferenced BNode"
|
432
|
+
end
|
433
|
+
|
434
|
+
prop_list = order_properties(properties)
|
435
|
+
#add_debug "=> property order: #{prop_list.to_sentence}"
|
436
|
+
|
437
|
+
prop_list.each do |prop|
|
438
|
+
predicate = RDF::URI.intern(prop)
|
439
|
+
|
440
|
+
p_iri = format_uri(predicate, :position => :predicate)
|
441
|
+
@depth += 1
|
442
|
+
defn[p_iri] = property(predicate, properties[prop])
|
443
|
+
add_debug "prop(#{p_iri}) => #{properties[prop]} => #{defn[p_iri].inspect}"
|
444
|
+
@depth -= 1
|
445
|
+
end
|
446
|
+
|
447
|
+
add_debug "subject: #{subject} has defn: #{defn.inspect}"
|
448
|
+
defn
|
449
|
+
end
|
450
|
+
|
451
|
+
##
|
452
|
+
# Serialize objects for a property
|
453
|
+
#
|
454
|
+
# Spec confusion: sorting of multi-valued properties not adequately specified.
|
455
|
+
#
|
456
|
+
# @param [RDF::URI] predicate
|
457
|
+
# @param [Array<RDF::URI>, RDF::URI] objects
|
458
|
+
# @param [Hash{Symbol => Object}] options
|
459
|
+
# @return [Array, Hash, Object]
|
460
|
+
def property(predicate, objects, options = {})
|
461
|
+
objects = objects.first if objects.is_a?(Array) && objects.length == 1
|
462
|
+
case objects
|
463
|
+
when Array
|
464
|
+
objects.sort_by(&:to_s).map {|o| property(predicate, o, options)}
|
465
|
+
when RDF::Literal
|
466
|
+
format_literal(objects, options.merge(:property => predicate))
|
467
|
+
else
|
468
|
+
if is_valid_list?(objects)
|
469
|
+
format_list(objects, :property => predicate)
|
470
|
+
elsif is_done?(objects) || !@subjects.include?(objects)
|
471
|
+
format_uri(objects, :position => :object, :property => predicate)
|
472
|
+
else
|
473
|
+
subject(objects, :property => predicate)
|
474
|
+
end
|
475
|
+
end
|
476
|
+
end
|
477
|
+
|
478
|
+
##
|
479
|
+
# Return a CURIE for the IRI, or nil. Adds namespace of CURIE to defined prefixes
|
480
|
+
# @param [RDF::Resource] resource
|
481
|
+
# @return [String, nil] value to use to identify IRI
|
482
|
+
def get_curie(resource)
|
483
|
+
add_debug "get_curie(#{resource.inspect})"
|
484
|
+
case resource
|
485
|
+
when RDF::Node
|
486
|
+
return resource.to_s
|
487
|
+
when RDF::URI
|
488
|
+
iri = resource.to_s
|
489
|
+
return iri if options[:canonicalize]
|
490
|
+
else
|
491
|
+
return nil
|
492
|
+
end
|
493
|
+
|
494
|
+
curie = case
|
495
|
+
when @iri_to_curie.has_key?(iri)
|
496
|
+
return @iri_to_curie[iri]
|
497
|
+
when u = @iri_to_prefix.keys.detect {|u| iri.index(u.to_s) == 0}
|
498
|
+
# Use a defined prefix
|
499
|
+
prefix = @iri_to_prefix[u]
|
500
|
+
prefix(prefix, u) # Define for output
|
501
|
+
iri.sub(u.to_s, "#{prefix}:")
|
502
|
+
when @options[:standard_prefixes] && vocab = RDF::Vocabulary.detect {|v| iri.index(v.to_uri.to_s) == 0}
|
503
|
+
prefix = vocab.__name__.to_s.split('::').last.downcase
|
504
|
+
@iri_to_prefix[vocab.to_uri.to_s] = prefix
|
505
|
+
prefix(prefix, vocab.to_uri) # Define for output
|
506
|
+
iri.sub(vocab.to_uri.to_s, "#{prefix}:")
|
507
|
+
else
|
508
|
+
nil
|
509
|
+
end
|
510
|
+
|
511
|
+
@iri_to_curie[iri] = curie
|
512
|
+
rescue Addressable::URI::InvalidURIError => e
|
513
|
+
raise RDF::WriterError, "Invalid IRI #{resource.inspect}: #{e.message}"
|
514
|
+
end
|
515
|
+
|
516
|
+
##
|
517
|
+
# Take a hash from predicate IRIs to lists of values.
|
518
|
+
# Sort the lists of values. Return a sorted list of properties.
|
519
|
+
# @param [Hash{String => Array<Resource>}] properties A hash of Property to Resource mappings
|
520
|
+
# @return [Array<String>}] Ordered list of properties.
|
521
|
+
def order_properties(properties)
|
522
|
+
# Make sorted list of properties
|
523
|
+
prop_list = []
|
524
|
+
|
525
|
+
properties.keys.sort do |a,b|
|
526
|
+
format_uri(a, :position => :predicate) <=> format_uri(b, :position => :predicate)
|
527
|
+
end.each do |prop|
|
528
|
+
prop_list << prop.to_s
|
529
|
+
end
|
530
|
+
|
531
|
+
prop_list
|
532
|
+
end
|
533
|
+
|
534
|
+
# Order subjects for output. Override this to output subjects in another order.
|
535
|
+
#
|
536
|
+
# Uses #base_uri.
|
537
|
+
# @return [Array<Resource>] Ordered list of subjects
|
538
|
+
def order_subjects
|
539
|
+
seen = {}
|
540
|
+
subjects = []
|
541
|
+
|
542
|
+
return @subjects.keys.sort do |a,b|
|
543
|
+
format_iri(a, :position => :subject) <=> format_iri(b, :position => :subject)
|
544
|
+
end if @options[:canonicalize]
|
545
|
+
|
546
|
+
# Start with base_uri
|
547
|
+
if base_uri && @subjects.keys.include?(base_uri)
|
548
|
+
subjects << base_uri
|
549
|
+
seen[base_uri] = true
|
550
|
+
end
|
551
|
+
|
552
|
+
# Sort subjects by resources over bnodes, ref_counts and the subject URI itself
|
553
|
+
recursable = @subjects.keys.
|
554
|
+
select {|s| !seen.include?(s)}.
|
555
|
+
map {|r| [r.is_a?(RDF::Node) ? 1 : 0, ref_count(r), r]}.
|
556
|
+
sort
|
557
|
+
|
558
|
+
subjects += recursable.map{|r| r.last}
|
559
|
+
end
|
560
|
+
|
561
|
+
# Return the number of times this node has been referenced in the object position
|
562
|
+
# @return [Integer]
|
563
|
+
def ref_count(node)
|
564
|
+
@references.fetch(node, 0)
|
565
|
+
end
|
566
|
+
|
567
|
+
##
|
568
|
+
# Does predicate have a range of IRI?
|
569
|
+
# @param [RDF::URI] predicate
|
570
|
+
# @return [Boolean]
|
571
|
+
def iri_range?(predicate)
|
572
|
+
return false if predicate.nil? || @options[:canonicalize]
|
573
|
+
|
574
|
+
unless coerce.has_key?(predicate)
|
575
|
+
# objects of all statements with the predicate may not be literal
|
576
|
+
coerce[predicate] = @graph.query(:predicate => predicate).to_a.any? {|st| st.object.literal?} ?
|
577
|
+
false : RDF::XSD.anyURI
|
578
|
+
end
|
579
|
+
|
580
|
+
add_debug "iri_range(#{predicate}) = #{coerce[predicate].inspect}"
|
581
|
+
coerce[predicate] == RDF::XSD.anyURI
|
582
|
+
end
|
583
|
+
|
584
|
+
##
|
585
|
+
# Does predicate have a range of specific typed literal?
|
586
|
+
# @param [RDF::URI] predicate
|
587
|
+
# @return [Boolean]
|
588
|
+
def datatype_range?(predicate)
|
589
|
+
unless coerce.has_key?(predicate)
|
590
|
+
# objects of all statements with the predicate must be literal
|
591
|
+
# and have the same non-nil datatype
|
592
|
+
dt = nil
|
593
|
+
@graph.query(:predicate => predicate) do |st|
|
594
|
+
if st.object.literal? && st.object.has_datatype?
|
595
|
+
dt = st.object.datatype if dt.nil?
|
596
|
+
dt = false unless dt == st.object.datatype
|
597
|
+
else
|
598
|
+
dt = false
|
599
|
+
end
|
600
|
+
end
|
601
|
+
add_debug "range(#{predicate}) = #{dt.inspect}"
|
602
|
+
coerce[predicate] = dt
|
603
|
+
end
|
604
|
+
|
605
|
+
coerce[predicate]
|
606
|
+
end
|
607
|
+
|
608
|
+
# Reset internal helper instance variables
|
609
|
+
def reset
|
610
|
+
@depth = 0
|
611
|
+
@references = {}
|
612
|
+
@serialized = {}
|
613
|
+
@subjects = {}
|
614
|
+
@iri_to_curie = {}
|
615
|
+
end
|
616
|
+
|
617
|
+
# Add debug event to debug array, if specified
|
618
|
+
#
|
619
|
+
# @param [String] message::
|
620
|
+
def add_debug(message)
|
621
|
+
msg = "#{" " * @depth * 2}#{message}"
|
622
|
+
STDERR.puts msg if ::JSON::LD::debug?
|
623
|
+
@debug << msg if @debug.is_a?(Array)
|
624
|
+
end
|
625
|
+
|
626
|
+
# Checks if l is a valid RDF list, i.e. no nodes have other properties.
|
627
|
+
def is_valid_list?(l)
|
628
|
+
props = @graph.properties(l)
|
629
|
+
unless l.node? && props.has_key?(RDF.first.to_s) || l == RDF.nil
|
630
|
+
add_debug "is_valid_list: false, #{l.inspect}: #{props.inspect}"
|
631
|
+
return false
|
632
|
+
end
|
633
|
+
|
634
|
+
while l && l != RDF.nil do
|
635
|
+
#add_debug "is_valid_list(length): #{props.length}"
|
636
|
+
return false unless props.has_key?(RDF.first.to_s) && props.has_key?(RDF.rest.to_s)
|
637
|
+
n = props[RDF.rest.to_s]
|
638
|
+
unless n.is_a?(Array) && n.length == 1
|
639
|
+
add_debug "is_valid_list: false, #{n.inspect}"
|
640
|
+
return false
|
641
|
+
end
|
642
|
+
l = n.first
|
643
|
+
unless l.node? || l == RDF.nil
|
644
|
+
add_debug "is_valid_list: false, #{l.inspect}"
|
645
|
+
return false
|
646
|
+
end
|
647
|
+
props = @graph.properties(l)
|
648
|
+
end
|
649
|
+
add_debug "is_valid_list: valid"
|
650
|
+
true
|
651
|
+
end
|
652
|
+
|
653
|
+
def is_done?(subject)
|
654
|
+
@serialized.include?(subject)
|
655
|
+
end
|
656
|
+
|
657
|
+
# Mark a subject as done.
|
658
|
+
def subject_done(subject)
|
659
|
+
@serialized[subject] = true
|
660
|
+
end
|
9
661
|
end
|
10
662
|
end
|
11
663
|
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: json-ld
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.0.
|
5
|
+
version: 0.0.4
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Gregg Kellogg
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-
|
13
|
+
date: 2011-06-29 00:00:00 -07:00
|
14
14
|
default_executable:
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
@@ -19,7 +19,7 @@ dependencies:
|
|
19
19
|
requirement: &id001 !ruby/object:Gem::Requirement
|
20
20
|
none: false
|
21
21
|
requirements:
|
22
|
-
- -
|
22
|
+
- - ">="
|
23
23
|
- !ruby/object:Gem::Version
|
24
24
|
version: 0.3.3
|
25
25
|
type: :runtime
|
@@ -63,11 +63,66 @@ dependencies:
|
|
63
63
|
requirement: &id005 !ruby/object:Gem::Requirement
|
64
64
|
none: false
|
65
65
|
requirements:
|
66
|
-
- -
|
66
|
+
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: 0.3.2
|
69
69
|
type: :development
|
70
70
|
version_requirements: *id005
|
71
|
+
- !ruby/object:Gem::Dependency
|
72
|
+
name: rdf-n3
|
73
|
+
prerelease: false
|
74
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
75
|
+
none: false
|
76
|
+
requirements:
|
77
|
+
- - ">="
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: 0.3.3
|
80
|
+
type: :development
|
81
|
+
version_requirements: *id006
|
82
|
+
- !ruby/object:Gem::Dependency
|
83
|
+
name: rdf-isomorphic
|
84
|
+
prerelease: false
|
85
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
86
|
+
none: false
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: 0.3.4
|
91
|
+
type: :development
|
92
|
+
version_requirements: *id007
|
93
|
+
- !ruby/object:Gem::Dependency
|
94
|
+
name: sxp
|
95
|
+
prerelease: false
|
96
|
+
requirement: &id008 !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ">="
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: "0"
|
102
|
+
type: :development
|
103
|
+
version_requirements: *id008
|
104
|
+
- !ruby/object:Gem::Dependency
|
105
|
+
name: sparql-algebra
|
106
|
+
prerelease: false
|
107
|
+
requirement: &id009 !ruby/object:Gem::Requirement
|
108
|
+
none: false
|
109
|
+
requirements:
|
110
|
+
- - ">="
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
version: "0"
|
113
|
+
type: :development
|
114
|
+
version_requirements: *id009
|
115
|
+
- !ruby/object:Gem::Dependency
|
116
|
+
name: sparql-grammar
|
117
|
+
prerelease: false
|
118
|
+
requirement: &id010 !ruby/object:Gem::Requirement
|
119
|
+
none: false
|
120
|
+
requirements:
|
121
|
+
- - ">="
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
version: "0"
|
124
|
+
type: :development
|
125
|
+
version_requirements: *id010
|
71
126
|
description: JSON-LD reader/writer for Ruby.
|
72
127
|
email: public-rdf-ruby@w3.org
|
73
128
|
executables: []
|
@@ -81,6 +136,7 @@ files:
|
|
81
136
|
- README
|
82
137
|
- UNLICENSE
|
83
138
|
- VERSION
|
139
|
+
- lib/json/ld/extensions.rb
|
84
140
|
- lib/json/ld/format.rb
|
85
141
|
- lib/json/ld/reader.rb
|
86
142
|
- lib/json/ld/version.rb
|