@@ -82,12 +82,15 @@ class API
8282 # Use unique bnode identifiers, defaults to using the identifier which the node was originally initialized with (if any).
8383 # @option options [Symbol] :adapter used with MultiJson
8484 # @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.
8587 # @yield [api]
8688 # @yieldparam [API]
8789 # @raise [JsonLdError]
8890 def initialize ( input , context , rename_bnodes : true , unique_bnodes : false , **options , &block )
8991 @options = {
9092 compactArrays : true ,
93+ ordered : false ,
9194 documentLoader : self . class . method ( :documentLoader )
9295 } . merge ( options )
9396 @namer = unique_bnodes ? BlankNodeUniqer . new : ( rename_bnodes ? BlankNodeNamer . new ( "b" ) : BlankNodeMapper . new )
@@ -163,11 +166,11 @@ def initialize(input, context, rename_bnodes: true, unique_bnodes: false, **opti
163166 # @return [Object, Array<Hash>]
164167 # If a block is given, the result of evaluating the block is returned, otherwise, the expanded JSON-LD document
165168 # @see http://json-ld.org/spec/latest/json-ld-api/#expansion-algorithm
166- def self . expand ( input , ordered : true , framing : false , **options , &block )
169+ def self . expand ( input , framing : false , **options , &block )
167170 result , doc_base = nil
168171 API . new ( input , options [ :expandContext ] , options ) do
169172 result = self . expand ( self . value , nil , self . context ,
170- ordered : ordered ,
173+ ordered : @options [ : ordered] ,
171174 framing : framing )
172175 doc_base = @options [ :base ]
173176 end
@@ -218,14 +221,14 @@ def self.compact(input, context, expanded: false, **options)
218221
219222 # 1) Perform the Expansion Algorithm on the JSON-LD input.
220223 # This removes any existing context to allow the given context to be cleanly applied.
221- 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 |
222225 options [ :base ] ||= base_iri if options [ :compactToRelative ]
223226 res
224227 end
225228
226229 API . new ( expanded_input , context , no_default_base : true , **options ) do
227230 log_debug ( ".compact" ) { "expanded input: #{ expanded_input . to_json ( JSON_STATE ) rescue 'malformed json' } " }
228- result = compact ( value )
231+ result = compact ( value , ordered : @options [ :ordered ] )
229232
230233 # xxx) Add the given context to the output
231234 ctx = self . context . serialize
@@ -276,23 +279,23 @@ def self.flatten(input, context, expanded: false, **options)
276279 create_node_map ( value , graph_maps )
277280
278281 default_graph = graph_maps [ '@default' ]
279- graph_maps . keys . sort . each do |graph_name |
282+ graph_maps . keys . opt_sort ( ordered : @options [ :ordered ] ) . each do |graph_name |
280283 next if graph_name == '@default'
281284
282285 graph = graph_maps [ graph_name ]
283286 entry = default_graph [ graph_name ] ||= { '@id' => graph_name }
284287 nodes = entry [ '@graph' ] ||= [ ]
285- graph . keys . sort . each do |id |
288+ graph . keys . opt_sort ( ordered : @options [ :ordered ] ) . each do |id |
286289 nodes << graph [ id ] unless node_reference? ( graph [ id ] )
287290 end
288291 end
289- default_graph . keys . sort . each do |id |
292+ default_graph . keys . opt_sort ( ordered : @options [ :ordered ] ) . each do |id |
290293 flattened << default_graph [ id ] unless node_reference? ( default_graph [ id ] )
291294 end
292295
293296 if context && !flattened . empty?
294297 # 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.
295- compacted = as_array ( compact ( flattened ) )
298+ compacted = as_array ( compact ( flattened , ordered : @options [ :ordered ] ) )
296299 kwgraph = self . context . compact_iri ( '@graph' , quiet : true )
297300 flattened = self . context . serialize . merge ( kwgraph => compacted )
298301 end
@@ -397,7 +400,7 @@ def self.frame(input, frame, expanded: false, **options)
397400 framing_state [ :subjects ] = framing_state [ :graphMap ] [ framing_state [ :graph ] ]
398401
399402 result = [ ]
400- 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 )
401404
402405 # Count blank node identifiers used in the document, if pruning
403406 unless @options [ :processingMode ] == 'json-ld-1.0'
@@ -408,7 +411,7 @@ def self.frame(input, frame, expanded: false, **options)
408411 # Initalize context from frame
409412 @context = @context . parse ( frame [ '@context' ] )
410413 # Compact result
411- compacted = compact ( result )
414+ compacted = compact ( result , ordered : @options [ :ordered ] )
412415 compacted = [ compacted ] unless options [ :omitGraph ] || compacted . is_a? ( Array )
413416
414417 # Add the given context to the output
@@ -493,8 +496,11 @@ def self.toRdf(input, expanded: false, **options, &block)
493496 def self . fromRdf ( input , useRdfType : false , useNativeTypes : false , **options , &block )
494497 result = nil
495498
496- API . new ( nil , nil , options ) do |api |
497- 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 ] )
498504 end
499505
500506 block_given? ? yield ( result ) : result
0 commit comments