Preserve key order loading YAML from a file in Rub

2019-05-06 14:29发布

问题:

I want to preserve the order of the keys in a YAML file loaded from disk, processed in some way and written back to disk.

Here is a basic example of loading YAML in Ruby (v1.8.7):

require 'yaml'

configuration = nil
File.open('configuration.yaml', 'r') do |file|
  configuration = YAML::load(file)
  # at this point configuration is a hash with keys in an undefined order
end

# process configuration in some way

File.open('output.yaml', 'w+') do |file|
  YAML::dump(configuration, file)
end

Unfortunately, this will destroy the order of the keys in configuration.yaml once the hash is built. I cannot find a way of controlling what data structure is used by YAML::load(), e.g. alib's orderedmap.

I've had no luck searching the web for a solution.

回答1:

If you're stuck using 1.8.7 for whatever reason (like I am), I've resorted to using active_support/ordered_hash. I know activesupport seems like a big include, but they've refactored it in later versions to where you pretty much only require the part you need in the file and the rest gets left out. Just gem install activesupport, and include it as shown below. Also, in your YAML file, be sure to use an !!omap declaration (and an array of Hashes). Example time!

# config.yml #

months: !!omap
  - january: enero
  - february: febrero
  - march: marzo
  - april: abril
  - may: mayo

Here's what the Ruby behind it looks like.

# loader.rb #

require 'yaml'
require 'active_support/ordered_hash'

# Load up the file into a Hash
config = File.open('config.yml','r') { |f| YAML::load f }

# So long as you specified an !!omap, this is actually a
# YAML::PrivateClass, an array of Hashes
puts config['months'].class

# Parse through its value attribute, stick results in an OrderedHash,
# and reassign it to our hash
ordered = ActiveSupport::OrderedHash.new
config['months'].value.each { |m| ordered[m.keys.first] = m.values.first }
config['months'] = ordered

I'm looking for a solution that allows me to recursively dig through a Hash loaded from a .yml file, look for those YAML::PrivateClass objects, and convert them into an ActiveSupport::OrderedHash. I may post a question on that.



回答2:

Use Ruby 1.9.x. Previous version of Ruby do not preserve the order of Hash keys, but 1.9 does.



回答3:

Someone came up with the same issue. There is a gem ordered hash. Note that it is not a hash, it creates a subclass of hash. You might give it a try, but if you see a problem dealing with YAML, then you should consider upgrading to ruby1.9.



标签: ruby load key yaml