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