Почему YAML выдает плавающие ArgumentErrors на строках?

У меня есть несколько вложенных строк в сложном хеше, который вызывает исключения "ArgumentError". Как лучше всего справляться с этим?

require 'yaml'
{
    a: 'hello',
    b: [{f:'hello',g:Hash.new,i:{a:'hello'}}],
    c: {e:"+."}
}.to_yaml #=> `Float': invalid value for Float(): "+" (ArgumentError) 

Полный дамп ошибок:

/Users/XXX/.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/psych/scalar_scanner.rb:99:in `Float': invalid value for Float(): "+" (ArgumentError)
    from /Users/XXX/.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/psych/scalar_scanner.rb:99:in `tokenize'
    from /Users/XXX/.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/psych/visitors/yaml_tree.rb:272:in `visit_String'
    from /Users/XXX/.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/psych/visitors/yaml_tree.rb:128:in `accept'
    from /Users/XXX/.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/psych/visitors/yaml_tree.rb:324:in `block in visit_Hash'
    from /Users/XXX/.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/psych/visitors/yaml_tree.rb:322:in `each'
    from /Users/XXX/.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/psych/visitors/yaml_tree.rb:322:in `visit_Hash'
    from /Users/XXX/.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/psych/visitors/yaml_tree.rb:128:in `accept'
    from /Users/XXX/.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/psych/visitors/yaml_tree.rb:324:in `block in visit_Hash'
    from /Users/XXX/.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/psych/visitors/yaml_tree.rb:322:in `each'
    from /Users/XXX/.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/psych/visitors/yaml_tree.rb:322:in `visit_Hash'
    from /Users/XXX/.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/psych/visitors/yaml_tree.rb:128:in `accept'
    from /Users/XXX/.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/psych/visitors/yaml_tree.rb:92:in `push'
    from /Users/XXX/.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/psych.rb:244:in `dump'
    from /Users/XXX/.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/psych/core_ext.rb:14:in `psych_to_yaml'

2 ответа

Это похоже на ошибку в комплекте психики. Заделка ~/.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/psych/scalar_scanner.rb в строке 99 из:

Float(string.gsub(/[,_]|\.$/, ''))

чтобы:

Float(string.gsub(/[,_]|\.$/, '')) rescue ArgumentError

устраняет проблему По сути, это то, что находится в самоцвете психики, а также в комплектной версии Ruby 1.9.

Если вы не хотите вносить исправления в свой Ruby, используйте гем psych-1.3.4; просто обязательно require 'psych' скорее, чем 'yaml':

gem 'psych', '=1.3.4'
require 'psych'
{a: 'hello', b: [{f:'hello',g:Hash.new,i:{a:'hello'}}], c: {e:"0+."}}.to_yaml
# => "---\n:a: hello\n:b:\n- :f: hello\n  :g: {}\n  :i:\n    :a: hello\n:c:\n  :e: 0+.\n"

Это может быть воспроизведено на более простом примере:

"+.".to_yaml

Похоже, что это ошибка в версии psych в комплекте с ruby ​​2.0.0 (я уверен, что в других версиях):

  when FLOAT
    if string == '.'
      @string_cache[string] = true
      string
    else
      Float(string.gsub(/[,_]|\.$/, ''))
    end

Проблема в том что "+". выглядит как действительное число с плавающей запятой, как +.5.

Это исправлено в Ruby 2.2.1 (или, возможно, более ранней версии), которая специально проверяет случай, когда может присутствовать начальный знак (+ или -):

  when FLOAT
    if string =~ /\A[-+]?\.\Z/
      @string_cache[string] = true
      string
    else
      Float(string.gsub(/[,_]|\.$/, ''))
    end
Другие вопросы по тегам