lib/json/ld/api.rb in json-ld-3.0.0 vs lib/json/ld/api.rb in json-ld-3.0.1
- old
+ new
@@ -69,25 +69,30 @@
# The callback of the loader to be used to retrieve remote documents and contexts. If specified, it must be used to retrieve remote documents and contexts; otherwise, if not specified, the processor's built-in loader must be used. See {documentLoader} for the method signature.
# @option options [String, #read, Hash, Array, JSON::LD::Context] :expandContext
# A context that is used to initialize the active context when expanding a document.
# @option options [Boolean, String, RDF::URI] :flatten
# If set to a value that is not `false`, the JSON-LD processor must modify the output of the Compaction Algorithm or the Expansion Algorithm by coalescing all properties associated with each subject via the Flattening Algorithm. The value of `flatten must` be either an _IRI_ value representing the name of the graph to flatten, or `true`. If the value is `true`, then the first graph encountered in the input document is selected and flattened.
+ # @option options [String] :language
+ # When set, this has the effect of inserting a context definition with `@language` set to the associated value, creating a default language for interpreting string values.
# @option options [String] :processingMode
# Processing mode, json-ld-1.0 or json-ld-1.1.
# If `processingMode` is not specified, a mode of `json-ld-1.0` or `json-ld-1.1` is set, the context used for `expansion` or `compaction`.
# @option options [Boolean] :rename_bnodes (true)
# Rename bnodes as part of expansion, or keep them the same.
# @option options [Boolean] :unique_bnodes (false)
# Use unique bnode identifiers, defaults to using the identifier which the node was originally initialized with (if any).
# @option options [Symbol] :adapter used with MultiJson
# @option options [Boolean] :validate Validate input, if a string or readable object.
+ # @option options [Boolean] :ordered (true)
+ # Order traversal of dictionary members by key when performing algorithms.
# @yield [api]
# @yieldparam [API]
# @raise [JsonLdError]
def initialize(input, context, rename_bnodes: true, unique_bnodes: false, **options, &block)
@options = {
compactArrays: true,
+ ordered: false,
documentLoader: self.class.method(:documentLoader)
}.merge(options)
@namer = unique_bnodes ? BlankNodeUniqer.new : (rename_bnodes ? BlankNodeNamer.new("b") : BlankNodeMapper.new)
# For context via Link header
@@ -159,15 +164,15 @@
# The document base as determined during expansion
# @yieldreturn [Object] returned object
# @return [Object, Array<Hash>]
# If a block is given, the result of evaluating the block is returned, otherwise, the expanded JSON-LD document
# @see http://json-ld.org/spec/latest/json-ld-api/#expansion-algorithm
- def self.expand(input, ordered: true, framing: false, **options, &block)
+ def self.expand(input, framing: false, **options, &block)
result, doc_base = nil
API.new(input, options[:expandContext], options) do
result = self.expand(self.value, nil, self.context,
- ordered: ordered,
+ ordered: @options[:ordered],
framing: framing)
doc_base = @options[:base]
end
# If, after the algorithm outlined above is run, the resulting element is an JSON object with just a @graph property, element is set to the value of @graph's value.
@@ -214,18 +219,18 @@
result = nil
options = {compactToRelative: true}.merge(options)
# 1) Perform the Expansion Algorithm on the JSON-LD input.
# This removes any existing context to allow the given context to be cleanly applied.
- expanded_input = expanded ? input : API.expand(input, options) do |res, base_iri|
+ expanded_input = expanded ? input : API.expand(input, options.merge(ordered: false)) do |res, base_iri|
options[:base] ||= base_iri if options[:compactToRelative]
res
end
API.new(expanded_input, context, no_default_base: true, **options) do
log_debug(".compact") {"expanded input: #{expanded_input.to_json(JSON_STATE) rescue 'malformed json'}"}
- result = compact(value)
+ result = compact(value, ordered: @options[:ordered])
# xxx) Add the given context to the output
ctx = self.context.serialize
if result.is_a?(Array)
kwgraph = self.context.compact_iri('@graph', vocab: true, quiet: true)
@@ -272,27 +277,27 @@
# Initialize node map to a JSON object consisting of a single member whose key is @default and whose value is an empty JSON object.
graph_maps = {'@default' => {}}
create_node_map(value, graph_maps)
default_graph = graph_maps['@default']
- graph_maps.keys.sort.each do |graph_name|
+ graph_maps.keys.opt_sort(ordered: @options[:ordered]).each do |graph_name|
next if graph_name == '@default'
graph = graph_maps[graph_name]
entry = default_graph[graph_name] ||= {'@id' => graph_name}
nodes = entry['@graph'] ||= []
- graph.keys.sort.each do |id|
+ graph.keys.opt_sort(ordered: @options[:ordered]).each do |id|
nodes << graph[id] unless node_reference?(graph[id])
end
end
- default_graph.keys.sort.each do |id|
+ default_graph.keys.opt_sort(ordered: @options[:ordered]).each do |id|
flattened << default_graph[id] unless node_reference?(default_graph[id])
end
if context && !flattened.empty?
# Otherwise, return the result of compacting flattened according the Compaction algorithm passing context ensuring that the compaction result uses the @graph keyword (or its alias) at the top-level, even if the context is empty or if there is only one element to put in the @graph array. This ensures that the returned document has a deterministic structure.
- compacted = as_array(compact(flattened))
+ compacted = as_array(compact(flattened, ordered: @options[:ordered]))
kwgraph = self.context.compact_iri('@graph', quiet: true)
flattened = self.context.serialize.merge(kwgraph => compacted)
end
end
@@ -335,11 +340,10 @@
compactToRelative: true,
embed: '@last',
explicit: false,
requireAll: true,
omitDefault: false,
- omitGraph: false,
documentLoader: method(:documentLoader)
}.merge(options)
framing_state = {
graphMap: {},
@@ -359,23 +363,28 @@
else remote_doc.document
end
end
# Expand input to simplify processing
- expanded_input = expanded ? input : API.expand(input, options) do |res, base_iri|
+ expanded_input = expanded ? input : API.expand(input, options.merge(ordered: false)) do |res, base_iri|
options[:base] ||= base_iri if options[:compactToRelative]
res
end
# Expand frame to simplify processing
- expanded_frame = API.expand(frame, framing: true, **options)
+ expanded_frame = API.expand(frame, options.merge(framing: true, ordered: false))
# Initialize input using frame as context
API.new(expanded_input, frame['@context'], no_default_base: true, **options) do
log_debug(".frame") {"expanded input: #{expanded_input.to_json(JSON_STATE) rescue 'malformed json'}"}
log_debug(".frame") {"expanded frame: #{expanded_frame.to_json(JSON_STATE) rescue 'malformed json'}"}
+ # Set omitGraph option, if not present, based on processingMode
+ unless options.has_key?(:omitGraph)
+ options[:omitGraph] = @options[:processingMode] != 'json-ld-1.0'
+ end
+
# Get framing nodes from expanded input, replacing Blank Node identifiers as necessary
create_node_map(value, framing_state[:graphMap], active_graph: '@default')
frame_keys = frame.keys.map {|k| context.expand_iri(k, vocab: true, quiet: true)}
if frame_keys.include?('@graph')
@@ -389,22 +398,22 @@
end
framing_state[:subjects] = framing_state[:graphMap][framing_state[:graph]]
result = []
- frame(framing_state, framing_state[:subjects].keys.sort, (expanded_frame.first || {}), parent: result, **options)
+ frame(framing_state, framing_state[:subjects].keys.opt_sort(ordered: @options[:ordered]), (expanded_frame.first || {}), parent: result, **options)
# Count blank node identifiers used in the document, if pruning
unless @options[:processingMode] == 'json-ld-1.0'
bnodes_to_clear = count_blank_node_identifiers(result).collect {|k, v| k if v == 1}.compact
result = prune_bnodes(result, bnodes_to_clear)
end
# Initalize context from frame
@context = @context.parse(frame['@context'])
# Compact result
- compacted = compact(result)
+ compacted = compact(result, ordered: @options[:ordered])
compacted = [compacted] unless options[:omitGraph] || compacted.is_a?(Array)
# Add the given context to the output
result = if !compacted.is_a?(Array)
context.serialize.merge(compacted)
@@ -441,11 +450,11 @@
end
return results
end
# Expand input to simplify processing
- expanded_input = expanded ? input : API.expand(input, ordered: false, **options)
+ expanded_input = expanded ? input : API.expand(input, options.merge(ordered: false))
API.new(expanded_input, nil, options) do
# 1) Perform the Expansion Algorithm on the JSON-LD input.
# This removes any existing context to allow the given context to be cleanly applied.
log_debug(".toRdf") {"expanded input: #{expanded_input.to_json(JSON_STATE) rescue 'malformed json'}"}
@@ -485,11 +494,14 @@
# @return [Object, Hash]
# If a block is given, the result of evaluating the block is returned, otherwise, the expanded JSON-LD document
def self.fromRdf(input, useRdfType: false, useNativeTypes: false, **options, &block)
result = nil
- API.new(nil, nil, options) do |api|
- result = api.from_statements(input, useRdfType: useRdfType, useNativeTypes: useNativeTypes)
+ API.new(nil, nil, options) do
+ result = from_statements(input,
+ useRdfType: useRdfType,
+ useNativeTypes: useNativeTypes,
+ ordered: @options[:ordered])
end
block_given? ? yield(result) : result
end