Skip to content

Commit 6cf3cd1

Browse files
committed
Add serializer option to API methods and Writer to be able to specify the proc to use when serializing to JSON.
1 parent 29b5ab2 commit 6cf3cd1

File tree

5 files changed

+105
-24
lines changed

5 files changed

+105
-24
lines changed

lib/json/ld/api.rb

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,9 @@ def initialize(input, context, **options, &block)
155155
#
156156
# @param [String, #read, Hash, Array] input
157157
# The JSON-LD object to copy and perform the expansion upon.
158+
# @param [Proc] serializer (nil)
159+
# A Serializer method used for generating the JSON serialization of the result. If absent, the internal Ruby objects are returned, which can be transformed to JSON externally via `#to_json`.
160+
# See {JSON::LD::API.serializer}.
158161
# @param [Hash{Symbol => Object}] options
159162
# @option options (see #initialize)
160163
# @raise [JsonLdError]
@@ -167,7 +170,7 @@ def initialize(input, context, **options, &block)
167170
# @return [Object, Array<Hash>]
168171
# If a block is given, the result of evaluating the block is returned, otherwise, the expanded JSON-LD document
169172
# @see https://www.w3.org/TR/json-ld11-api/#expansion-algorithm
170-
def self.expand(input, framing: false, **options, &block)
173+
def self.expand(input, framing: false, serializer: nil, **options, &block)
171174
result = doc_base = nil
172175
API.new(input, options[:expandContext], **options) do
173176
result = self.expand(self.value, nil, self.context,
@@ -180,6 +183,7 @@ def self.expand(input, framing: false, **options, &block)
180183

181184
# Finally, if element is a JSON object, it is wrapped into an array.
182185
result = [result].compact unless result.is_a?(Array)
186+
result = serializer.call(result) if serializer
183187

184188
if block_given?
185189
case block.arity
@@ -204,6 +208,9 @@ def self.expand(input, framing: false, **options, &block)
204208
# The JSON-LD object to copy and perform the compaction upon.
205209
# @param [String, #read, Hash, Array, JSON::LD::Context] context
206210
# The base context to use when compacting the input.
211+
# @param [Proc] serializer (nil)
212+
# A Serializer instance used for generating the JSON serialization of the result. If absent, the internal Ruby objects are returned, which can be transformed to JSON externally via `#to_json`.
213+
# See {JSON::LD::API.serializer}.
207214
# @param [Boolean] expanded (false) Input is already expanded
208215
# @param [Hash{Symbol => Object}] options
209216
# @option options (see #initialize)
@@ -215,7 +222,7 @@ def self.expand(input, framing: false, **options, &block)
215222
# If a block is given, the result of evaluating the block is returned, otherwise, the compacted JSON-LD document
216223
# @raise [JsonLdError]
217224
# @see https://www.w3.org/TR/json-ld11-api/#compaction-algorithm
218-
def self.compact(input, context, expanded: false, **options)
225+
def self.compact(input, context, expanded: false, serializer: nil, **options)
219226
result = nil
220227
options = {compactToRelative: true}.merge(options)
221228

@@ -238,6 +245,7 @@ def self.compact(input, context, expanded: false, **options)
238245
end
239246
result = ctx.merge(result) unless ctx.fetch('@context', {}).empty?
240247
end
248+
result = serializer.call(result) if serializer
241249
block_given? ? yield(result) : result
242250
end
243251

@@ -251,6 +259,9 @@ def self.compact(input, context, expanded: false, **options)
251259
# @param [String, #read, Hash, Array, JSON::LD::EvaluationContext] context
252260
# An optional external context to use additionally to the context embedded in input when expanding the input.
253261
# @param [Boolean] expanded (false) Input is already expanded
262+
# @param [Proc] serializer (nil)
263+
# A Serializer instance used for generating the JSON serialization of the result. If absent, the internal Ruby objects are returned, which can be transformed to JSON externally via `#to_json`.
264+
# See {JSON::LD::API.serializer}.
254265
# @param [Hash{Symbol => Object}] options
255266
# @option options (see #initialize)
256267
# @option options [Boolean] :createAnnotations
@@ -262,7 +273,7 @@ def self.compact(input, context, expanded: false, **options)
262273
# @return [Object, Hash]
263274
# If a block is given, the result of evaluating the block is returned, otherwise, the flattened JSON-LD document
264275
# @see https://www.w3.org/TR/json-ld11-api/#framing-algorithm
265-
def self.flatten(input, context, expanded: false, **options)
276+
def self.flatten(input, context, expanded: false, serializer: nil, **options)
266277
flattened = []
267278
options = {
268279
compactToRelative: true,
@@ -318,6 +329,7 @@ def self.flatten(input, context, expanded: false, **options)
318329
end
319330
end
320331

332+
flattened = serializer.call(flattened) if serializer
321333
block_given? ? yield(flattened) : flattened
322334
end
323335

@@ -350,7 +362,7 @@ def self.flatten(input, context, expanded: false, **options)
350362
# If a block is given, the result of evaluating the block is returned, otherwise, the framed JSON-LD document
351363
# @raise [InvalidFrame]
352364
# @see https://www.w3.org/TR/json-ld11-api/#framing-algorithm
353-
def self.frame(input, frame, expanded: false, **options)
365+
def self.frame(input, frame, expanded: false, serializer: nil, **options)
354366
result = nil
355367
options = {
356368
base: (RDF::URI(input) if input.is_a?(String)),
@@ -467,6 +479,7 @@ def self.frame(input, frame, expanded: false, **options)
467479
result
468480
end
469481

482+
result = serializer.call(result) if serializer
470483
block_given? ? yield(result) : result
471484
end
472485

@@ -528,18 +541,21 @@ def self.toRdf(input, expanded: false, **options, &block)
528541
# The resulting `Array` is either returned or yielded, if a block is given.
529542
#
530543
# @param [RDF::Enumerable] input
544+
# @param [Boolean] useRdfType (false)
545+
# If set to `true`, the JSON-LD processor will treat `rdf:type` like a normal property instead of using `@type`.
546+
# @param [Boolean] useNativeTypes (false) use native representations
547+
# @param [Proc] serializer (nil)
548+
# A Serializer instance used for generating the JSON serialization of the result. If absent, the internal Ruby objects are returned, which can be transformed to JSON externally via `#to_json`.
549+
# See {JSON::LD::API.serializer}.
531550
# @param [Hash{Symbol => Object}] options
532551
# @option options (see #initialize)
533-
# @option options [Boolean] :useRdfType (false)
534-
# If set to `true`, the JSON-LD processor will treat `rdf:type` like a normal property instead of using `@type`.
535-
# @option options [Boolean] :useNativeTypes (false) use native representations
536552
# @yield jsonld
537553
# @yieldparam [Hash] jsonld
538554
# The JSON-LD document in expanded form
539555
# @yieldreturn [Object] returned object
540556
# @return [Object, Hash]
541557
# If a block is given, the result of evaluating the block is returned, otherwise, the expanded JSON-LD document
542-
def self.fromRdf(input, useRdfType: false, useNativeTypes: false, **options, &block)
558+
def self.fromRdf(input, useRdfType: false, useNativeTypes: false, serializer: nil, **options, &block)
543559
result = nil
544560

545561
API.new(nil, nil, **options) do
@@ -548,6 +564,7 @@ def self.fromRdf(input, useRdfType: false, useNativeTypes: false, **options, &bl
548564
useNativeTypes: useNativeTypes)
549565
end
550566

567+
result = serializer.call(result) if serializer
551568
block_given? ? yield(result) : result
552569
end
553570

@@ -794,6 +811,16 @@ def self.load_html(input, url:,
794811
raise JSON::LD::JsonLdError::InvalidScriptElement, e.message
795812
end
796813

814+
##
815+
# The default serializer for serialzing Ruby Objects to JSON.
816+
#
817+
# Defaults to `MultiJson.dump`
818+
#
819+
# @param [Object] object
820+
def self.serializer(object)
821+
MultiJson.dump(object, JSON_STATE)
822+
end
823+
797824
##
798825
# Validate JSON using JsonLint, if loaded
799826
private

lib/json/ld/to_rdf.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ module ToRDF
1111
##
1212
# @param [Hash{String => Object}] item
1313
# @param [RDF::Resource] graph_name
14-
# @param [Boolean] emitted triples are quoted triples.
14+
# @param [Boolean] quoted emitted triples are quoted triples.
1515
# @yield statement
1616
# @yieldparam [RDF::Statement] statement
1717
# @return RDF::Resource the subject of this item

lib/json/ld/writer.rb

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,8 @@ def default_context=(url); @default_context = url; end
229229
# frame to use when serializing.
230230
# @option options [Boolean] :unique_bnodes (false)
231231
# Use unique bnode identifiers, defaults to using the identifier which the node was originall initialized with (if any).
232+
# @option options [Proc] serializer (JSON::LD::API.serializer)
233+
# A Serializer method used for generating the JSON serialization of the result.
232234
# @option options [Boolean] :stream (false)
233235
# Do not attempt to optimize graph presentation, suitable for streaming large graphs.
234236
# @yield [writer] `self`
@@ -239,6 +241,7 @@ def default_context=(url); @default_context = url; end
239241
def initialize(output = $stdout, **options, &block)
240242
options[:base_uri] ||= options[:base] if options.key?(:base)
241243
options[:base] ||= options[:base_uri] if options.key?(:base_uri)
244+
@serializer = options.fetch(:serializer, JSON::LD::API.method(:serializer))
242245
super do
243246
@repo = RDF::Repository.new
244247

@@ -335,7 +338,7 @@ def write_epilogue
335338
result = API.compact(result, context, **@options)
336339
end
337340

338-
@output.write(result.to_json(JSON_STATE))
341+
@output.write(@serializer.call(result))
339342
end
340343

341344
super

spec/api_spec.rb

Lines changed: 64 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -56,22 +56,72 @@
5656
ttl = filename.sub(/-input\..*$/, '-rdf.ttl')
5757

5858
context test, skip: ("Not supported in JRuby" if RUBY_ENGINE == "jruby" && %w(oj yajl).include?(adapter.to_s)) do
59-
it "expands" do
60-
options = {logger: logger, adapter: adapter}
61-
options[:expandContext] = File.open(context) if context
62-
jld = described_class.expand(File.open(filename), **options)
63-
expect(jld).to produce_jsonld(JSON.load(File.open(expanded)), logger)
64-
end if File.exist?(expanded)
59+
if File.exist?(expanded)
60+
it "expands" do
61+
options = {logger: logger, adapter: adapter}
62+
File.open(context) do |ctx_io|
63+
File.open(filename) do |file|
64+
options[:expandContext] = ctx_io if context
65+
jld = described_class.expand(file, **options)
66+
expect(jld).to produce_jsonld(JSON.parse(File.read(expanded)), logger)
67+
end
68+
end
69+
end
70+
71+
it "expands with serializer" do
72+
options = {logger: logger, adapter: adapter}
73+
File.open(context) do |ctx_io|
74+
File.open(filename) do |file|
75+
options[:expandContext] = ctx_io if context
76+
jld = described_class.expand(file, serializer: JSON::LD::API.method(:serializer), **options)
77+
expect(jld).to be_a(String)
78+
expect(JSON.load(jld)).to produce_jsonld(JSON.parse(File.read(expanded)), logger)
79+
end
80+
end
81+
end
82+
end
6583

66-
it "compacts" do
67-
jld = described_class.compact(File.open(filename), File.open(context), adapter: adapter, logger: logger)
68-
expect(jld).to produce_jsonld(JSON.load(File.open(compacted)), logger)
69-
end if File.exist?(compacted) && File.exist?(context)
84+
if File.exist?(compacted) && File.exist?(context)
85+
it "compacts" do
86+
File.open(context) do |ctx_io|
87+
File.open(filename) do |file|
88+
jld = described_class.compact(file, ctx_io, adapter: adapter, logger: logger)
89+
expect(jld).to produce_jsonld(JSON.parse(File.read(compacted)), logger)
90+
end
91+
end
92+
end
93+
94+
it "compacts with serializer" do
95+
File.open(context) do |ctx_io|
96+
File.open(filename) do |file|
97+
jld = described_class.compact(file, ctx_io, serializer: JSON::LD::API.method(:serializer), adapter: adapter, logger: logger)
98+
expect(jld).to be_a(String)
99+
expect(JSON.load(jld)).to produce_jsonld(JSON.parse(File.read(compacted)), logger)
100+
end
101+
end
102+
end
103+
end
70104

71-
it "frame" do
72-
jld = described_class.frame(File.open(filename), File.open(frame), adapter: adapter, logger: logger)
73-
expect(jld).to produce_jsonld(JSON.load(File.open(framed)), logger)
74-
end if File.exist?(framed) && File.exist?(frame)
105+
if File.exist?(framed) && File.exist?(frame)
106+
it "frames" do
107+
File.open(frame) do |frame_io|
108+
File.open(filename) do |file|
109+
jld = described_class.frame(file, frame_io, adapter: adapter, logger: logger)
110+
expect(jld).to produce_jsonld(JSON.parse(File.read(framed)), logger)
111+
end
112+
end
113+
end
114+
115+
it "frames with serializer" do
116+
File.open(frame) do |frame_io|
117+
File.open(filename) do |file|
118+
jld = described_class.frame(file, frame_io, serializer: JSON::LD::API.method(:serializer), adapter: adapter, logger: logger)
119+
expect(jld).to be_a(String)
120+
expect(JSON.load(jld)).to produce_jsonld(JSON.parse(File.read(framed)), logger)
121+
end
122+
end
123+
end
124+
end
75125

76126
it "toRdf" do
77127
expect(RDF::Repository.load(filename, format: :jsonld, adapter: adapter, logger: logger)).to be_equivalent_graph(RDF::Repository.load(ttl), logger: logger)

spec/suite_helper.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ def self.open_file(filename_or_url, **options, &block)
6060
document_options[:headers][:content_type] = options[:contentType] if options[:contentType]
6161

6262
remote_document = RDF::Util::File::RemoteDocument.new(response.read, **document_options)
63+
response.close
6364
if block_given?
6465
return yield remote_document
6566
else

0 commit comments

Comments
 (0)