@@ -71,6 +71,8 @@ class API
7171 # A context that is used to initialize the active context when expanding a document.
7272 # @option options [Boolean, String, RDF::URI] :flatten
7373 # 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.
74+ # @option options [String] :language
75+ # 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.
7476 # @option options [String] :processingMode
7577 # Processing mode, json-ld-1.0 or json-ld-1.1.
7678 # 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`.
@@ -80,12 +82,15 @@ class API
8082 # Use unique bnode identifiers, defaults to using the identifier which the node was originally initialized with (if any).
8183 # @option options [Symbol] :adapter used with MultiJson
8284 # @option options [Boolean] :validate Validate input, if a string or readable object.
85+ # @option options [Boolean] :ordered (true)
86+ # Order traversal of dictionary members by key when performing algorithms.
8387 # @yield [api]
8488 # @yieldparam [API]
8589 # @raise [JsonLdError]
8690 def initialize ( input , context , rename_bnodes : true , unique_bnodes : false , **options , &block )
8791 @options = {
8892 compactArrays : true ,
93+ ordered : false ,
8994 documentLoader : self . class . method ( :documentLoader )
9095 } . merge ( options )
9196 @namer = unique_bnodes ? BlankNodeUniqer . new : ( rename_bnodes ? BlankNodeNamer . new ( "b" ) : BlankNodeMapper . new )
@@ -161,11 +166,11 @@ def initialize(input, context, rename_bnodes: true, unique_bnodes: false, **opti
161166 # @return [Object, Array<Hash>]
162167 # If a block is given, the result of evaluating the block is returned, otherwise, the expanded JSON-LD document
163168 # @see http://json-ld.org/spec/latest/json-ld-api/#expansion-algorithm
164- def self . expand ( input , ordered : true , framing : false , **options , &block )
169+ def self . expand ( input , framing : false , **options , &block )
165170 result , doc_base = nil
166171 API . new ( input , options [ :expandContext ] , options ) do
167172 result = self . expand ( self . value , nil , self . context ,
168- ordered : ordered ,
173+ ordered : @options [ : ordered] ,
169174 framing : framing )
170175 doc_base = @options [ :base ]
171176 end
@@ -216,14 +221,14 @@ def self.compact(input, context, expanded: false, **options)
216221
217222 # 1) Perform the Expansion Algorithm on the JSON-LD input.
218223 # This removes any existing context to allow the given context to be cleanly applied.
219- expanded_input = expanded ? input : API . expand ( input , options ) do |res , base_iri |
224+ expanded_input = expanded ? input : API . expand ( input , options . merge ( ordered : false ) ) do |res , base_iri |
220225 options [ :base ] ||= base_iri if options [ :compactToRelative ]
221226 res
222227 end
223228
224229 API . new ( expanded_input , context , no_default_base : true , **options ) do
225230 log_debug ( ".compact" ) { "expanded input: #{ expanded_input . to_json ( JSON_STATE ) rescue 'malformed json' } " }
226- result = compact ( value )
231+ result = compact ( value , ordered : @options [ :ordered ] )
227232
228233 # xxx) Add the given context to the output
229234 ctx = self . context . serialize
@@ -274,23 +279,23 @@ def self.flatten(input, context, expanded: false, **options)
274279 create_node_map ( value , graph_maps )
275280
276281 default_graph = graph_maps [ '@default' ]
277- graph_maps . keys . sort . each do |graph_name |
282+ graph_maps . keys . opt_sort ( ordered : @options [ :ordered ] ) . each do |graph_name |
278283 next if graph_name == '@default'
279284
280285 graph = graph_maps [ graph_name ]
281286 entry = default_graph [ graph_name ] ||= { '@id' => graph_name }
282287 nodes = entry [ '@graph' ] ||= [ ]
283- graph . keys . sort . each do |id |
288+ graph . keys . opt_sort ( ordered : @options [ :ordered ] ) . each do |id |
284289 nodes << graph [ id ] unless node_reference? ( graph [ id ] )
285290 end
286291 end
287- default_graph . keys . sort . each do |id |
292+ default_graph . keys . opt_sort ( ordered : @options [ :ordered ] ) . each do |id |
288293 flattened << default_graph [ id ] unless node_reference? ( default_graph [ id ] )
289294 end
290295
291296 if context && !flattened . empty?
292297 # 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.
293- compacted = as_array ( compact ( flattened ) )
298+ compacted = as_array ( compact ( flattened , ordered : @options [ :ordered ] ) )
294299 kwgraph = self . context . compact_iri ( '@graph' , quiet : true )
295300 flattened = self . context . serialize . merge ( kwgraph => compacted )
296301 end
@@ -337,7 +342,6 @@ def self.frame(input, frame, expanded: false, **options)
337342 explicit : false ,
338343 requireAll : true ,
339344 omitDefault : false ,
340- omitGraph : false ,
341345 documentLoader : method ( :documentLoader )
342346 } . merge ( options )
343347
@@ -361,19 +365,24 @@ def self.frame(input, frame, expanded: false, **options)
361365 end
362366
363367 # Expand input to simplify processing
364- expanded_input = expanded ? input : API . expand ( input , options ) do |res , base_iri |
368+ expanded_input = expanded ? input : API . expand ( input , options . merge ( ordered : false ) ) do |res , base_iri |
365369 options [ :base ] ||= base_iri if options [ :compactToRelative ]
366370 res
367371 end
368372
369373 # Expand frame to simplify processing
370- expanded_frame = API . expand ( frame , framing : true , ** options )
374+ expanded_frame = API . expand ( frame , options . merge ( framing : true , ordered : false ) )
371375
372376 # Initialize input using frame as context
373377 API . new ( expanded_input , frame [ '@context' ] , no_default_base : true , **options ) do
374378 log_debug ( ".frame" ) { "expanded input: #{ expanded_input . to_json ( JSON_STATE ) rescue 'malformed json' } " }
375379 log_debug ( ".frame" ) { "expanded frame: #{ expanded_frame . to_json ( JSON_STATE ) rescue 'malformed json' } " }
376380
381+ # Set omitGraph option, if not present, based on processingMode
382+ unless options . has_key? ( :omitGraph )
383+ options [ :omitGraph ] = @options [ :processingMode ] != 'json-ld-1.0'
384+ end
385+
377386 # Get framing nodes from expanded input, replacing Blank Node identifiers as necessary
378387 create_node_map ( value , framing_state [ :graphMap ] , active_graph : '@default' )
379388
@@ -391,7 +400,7 @@ def self.frame(input, frame, expanded: false, **options)
391400 framing_state [ :subjects ] = framing_state [ :graphMap ] [ framing_state [ :graph ] ]
392401
393402 result = [ ]
394- frame ( framing_state , framing_state [ :subjects ] . keys . sort , ( expanded_frame . first || { } ) , parent : result , **options )
403+ frame ( framing_state , framing_state [ :subjects ] . keys . opt_sort ( ordered : @options [ :ordered ] ) , ( expanded_frame . first || { } ) , parent : result , **options )
395404
396405 # Count blank node identifiers used in the document, if pruning
397406 unless @options [ :processingMode ] == 'json-ld-1.0'
@@ -402,7 +411,7 @@ def self.frame(input, frame, expanded: false, **options)
402411 # Initalize context from frame
403412 @context = @context . parse ( frame [ '@context' ] )
404413 # Compact result
405- compacted = compact ( result )
414+ compacted = compact ( result , ordered : @options [ :ordered ] )
406415 compacted = [ compacted ] unless options [ :omitGraph ] || compacted . is_a? ( Array )
407416
408417 # Add the given context to the output
@@ -443,7 +452,7 @@ def self.toRdf(input, expanded: false, **options, &block)
443452 end
444453
445454 # Expand input to simplify processing
446- expanded_input = expanded ? input : API . expand ( input , ordered : false , ** options )
455+ expanded_input = expanded ? input : API . expand ( input , options . merge ( ordered : false ) )
447456
448457 API . new ( expanded_input , nil , options ) do
449458 # 1) Perform the Expansion Algorithm on the JSON-LD input.
@@ -487,8 +496,11 @@ def self.toRdf(input, expanded: false, **options, &block)
487496 def self . fromRdf ( input , useRdfType : false , useNativeTypes : false , **options , &block )
488497 result = nil
489498
490- API . new ( nil , nil , options ) do |api |
491- result = api . from_statements ( input , useRdfType : useRdfType , useNativeTypes : useNativeTypes )
499+ API . new ( nil , nil , options ) do
500+ result = from_statements ( input ,
501+ useRdfType : useRdfType ,
502+ useNativeTypes : useNativeTypes ,
503+ ordered : @options [ :ordered ] )
492504 end
493505
494506 block_given? ? yield ( result ) : result
0 commit comments