I have 2 solutions.  My first golfish one which doesn't handle any
special cases:
----
require 'yaml'
require 'ostruct'
class Hash
  def to_os
    o=OpenStruct.new
    each{|k,v|o.send(k.to_s+'=',v.respond_to?(:to_os) ? v.to_os : v)}
    o
  end
end

if __FILE__ == $0
  p data=YAML::load(ARGF.read).to_os
end

And my second, which attempts to prevent recursion loops and adds a
'_' infront of invalid names or clashes with existing methods:

class Hash
  def to_os
    os = OpenStruct.new
    each {|key,val|
      key = '_'+key.to_s if !key.to_sym ||os.methods.include?(key.to_s)
      key = key.gsub(/[!?]/,'_')
      if val.object_id!=self.object_id
        os.send(key.to_s+'=', val.respond_to?(:to_os) ? val.to_os : val )
      end
    }
    os
  end
end

-Adam