lib/json/ld/api.rb in json-ld-3.1.3 vs lib/json/ld/api.rb in json-ld-3.1.4

- old
+ new

@@ -64,11 +64,11 @@ # # @param [String, #read, Hash, Array] input # @param [String, #read, Hash, Array, JSON::LD::Context] context # An external context to use additionally to the context embedded in input when expanding the input. # @param [Hash{Symbol => Object}] options - # @option options [String, #to_s] :base + # @option options [RDF::URI, String, #to_s] :base # The Base IRI to use when expanding the document. This overrides the value of `input` if it is a _IRI_. If not specified and `input` is not an _IRI_, the base IRI defaults to the current document IRI if in a browser context, or the empty string if there is no document context. If not specified, and a base IRI is found from `input`, options[:base] will be modified with this value. # @option options [Boolean] :compactArrays (true) # If set to `true`, the JSON-LD processor replaces arrays with just one element with that element during compaction. If set to `false`, all arrays will remain arrays even if they have just one element. # @option options [Boolean] :compactToRelative (true) # Creates document relative IRIs when compacting, if `true`, otherwise leaves expanded. @@ -106,20 +106,21 @@ ordered: false, extractAllScripts: false, }.merge(options) @namer = unique_bnodes ? BlankNodeUniqer.new : (rename_bnodes ? BlankNodeNamer.new("b") : BlankNodeMapper.new) + @options[:base] = RDF::URI(@options[:base]) if @options[:base] && !@options[:base].is_a?(RDF::URI) # For context via Link header _, context_ref = nil, nil @value = case input when Array, Hash then input.dup when IO, StringIO, String remote_doc = self.class.loadRemoteDocument(input, **@options) context_ref = remote_doc.contextUrl - @options[:base] = remote_doc.documentUrl if remote_doc.documentUrl && !@options[:no_default_base] + @options[:base] = RDF::URI(remote_doc.documentUrl) if remote_doc.documentUrl && !@options[:no_default_base] case remote_doc.document when String MultiJson.load(remote_doc.document, **options) else @@ -128,11 +129,11 @@ end end # If not provided, first use context from document, or from a Link header context ||= context_ref || {} - @context = Context.parse(context || {}, **@options) + @context = Context.parse(context, **@options) if block_given? case block.arity when 0, -1 then instance_eval(&block) else block.call(self) @@ -161,14 +162,13 @@ # @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 https://www.w3.org/TR/json-ld11-api/#expansion-algorithm def self.expand(input, framing: false, **options, &block) - result, doc_base = nil + result = doc_base = nil API.new(input, options[:expandContext], **options) do result = self.expand(self.value, nil, self.context, - 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. @@ -216,25 +216,25 @@ 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, ordered: false, **options) do |res, base_iri| - options[:base] ||= base_iri if options[:compactToRelative] + options[:base] ||= RDF::URI(base_iri) if base_iri && 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, ordered: @options[:ordered]) + result = compact(value) # xxx) Add the given context to the output - ctx = self.context.serialize + ctx = self.context.serialize(provided_context: context) if result.is_a?(Array) kwgraph = self.context.compact_iri('@graph', vocab: true) result = result.empty? ? {} : {kwgraph => result} end - result = ctx.merge(result) unless ctx.empty? + result = ctx.merge(result) unless ctx.fetch('@context', {}).empty? end block_given? ? yield(result) : result end ## @@ -263,11 +263,11 @@ extractAllScripts: true, }.merge(options) # Expand input to simplify processing expanded_input = expanded ? input : API.expand(input, **options) do |result, base_iri| - options[:base] ||= base_iri if options[:compactToRelative] + options[:base] ||= RDF::URI(base_iri) if base_iri && options[:compactToRelative] result end # Initialize input using API.new(expanded_input, context, no_default_base: true, **options) do @@ -292,13 +292,15 @@ 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, ordered: @options[:ordered])) + compacted = as_array(compact(flattened)) kwgraph = self.context.compact_iri('@graph') - flattened = self.context.serialize.merge(kwgraph => compacted) + flattened = self.context. + serialize(provided_context: context). + merge(kwgraph => compacted) end end block_given? ? yield(flattened) : flattened end @@ -333,11 +335,11 @@ # @raise [InvalidFrame] # @see https://www.w3.org/TR/json-ld11-api/#framing-algorithm def self.frame(input, frame, expanded: false, **options) result = nil options = { - base: (input if input.is_a?(String)), + base: (RDF::URI(input) if input.is_a?(String)), compactArrays: true, compactToRelative: true, embed: '@once', explicit: false, requireAll: false, @@ -367,11 +369,11 @@ end end # Expand input to simplify processing expanded_input = expanded ? input : API.expand(input, ordered: false, **options) do |res, base_iri| - options[:base] ||= base_iri if options[:compactToRelative] + options[:base] ||= RDF::URI(base_iri) if base_iri && options[:compactToRelative] res end # Expand frame to simplify processing expanded_frame = API.expand(frame, framing: true, ordered: false, **options) @@ -424,23 +426,26 @@ # Replace values with `@preserve` with the content of its entry. result = cleanup_preserve(result) log_debug(".frame") {"expanded result: #{result.to_json(JSON_STATE) rescue 'malformed json'}"} # Compact result - compacted = compact(result, ordered: @options[:ordered]) + compacted = compact(result) # @replace `@null` with nil, compacting arrays compacted = cleanup_null(compacted) 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) + compacted else kwgraph = context.compact_iri('@graph') - context.serialize.merge({kwgraph => compacted}) + {kwgraph => compacted} end + # Only add context if one was provided + result = context.serialize(provided_context: frame).merge(result) if frame['@context'] + log_debug(".frame") {"after compact: #{result.to_json(JSON_STATE) rescue 'malformed json'}"} result end block_given? ? yield(result) : result @@ -519,45 +524,47 @@ result = nil API.new(nil, nil, **options) do result = from_statements(input, useRdfType: useRdfType, - useNativeTypes: useNativeTypes, - ordered: @options[:ordered]) + useNativeTypes: useNativeTypes) end block_given? ? yield(result) : result end ## # Uses built-in or provided documentLoader to retrieve a parsed document. # # @param [RDF::URI, String] url + # @param [String, RDF::URI] base + # Location to use as documentUrl instead of `url`. + # @option options [Proc] :documentLoader + # The callback of the loader to be used to retrieve remote documents and contexts. # @param [Boolean] extractAllScripts # If set to `true`, when extracting JSON-LD script elements from HTML, unless a specific fragment identifier is targeted, extracts all encountered JSON-LD script elements using an array form, if necessary. # @param [String] profile # When the resulting `contentType` is `text/html` or `application/xhtml+xml`, this option determines the profile to use for selecting a JSON-LD script elements. # @param [String] requestProfile # One or more IRIs to use in the request as a profile parameter. - # @param [Boolean] validate + # @param [Boolean] validate (false) # Allow only appropriate content types - # @param [String, RDF::URI] base - # Location to use as documentUrl instead of `url`. # @param [Hash<Symbol => Object>] options # @yield remote_document # @yieldparam [RemoteDocumentRemoteDocument, RDF::Util::File::RemoteDocument] remote_document # @yieldreturn [Object] returned object # @return [Object, RemoteDocument] # If a block is given, the result of evaluating the block is returned, otherwise, the retrieved remote document and context information unless block given # @raise [JsonLdError] def self.loadRemoteDocument(url, + base: nil, + documentLoader: nil, extractAllScripts: false, profile: nil, requestProfile: nil, validate: false, - base: nil, **options) - documentLoader = options.fetch(:documentLoader, self.method(:documentLoader)) + documentLoader ||= self.method(:documentLoader) options = OPEN_OPTS.merge(options) if requestProfile # Add any request profile options[:headers]['Accept'] = options[:headers]['Accept'].sub('application/ld+json,', "application/ld+json;profile=#{requestProfile}, application/ld+json;q=0.9,") end