Perl和Ruby之间的YAML数据交换问题

时间:2012-09-26 20:50:38

标签: ruby perl yaml

我在使用YAML在Perl和Ruby之间交换数据时遇到了麻烦。我有一些看起来像数字的值:数字,例如1:16

Perl的YAML库(Tiny和XS)将其编码为1:16,不带引号。 Ruby的YAML库(Psych)不会将其解释为字符串,而是以某种方式成为Fixnum值4560。我无法弄清楚如何修复任何一方的转换问题。

我的用例的YAML中的每个值都应该是一个对象或字符串。所以,如果存在这样的选项,我可以告诉Perl YAML库引用所有值。或者有没有办法告诉Ruby YAML库将所有值都解释为字符串?有任何想法吗?

更改任何一方的语言在逻辑上都不是一种选择。

的Perl:

use YAML::XS qw(DumpFile);
my $foo={'abc'=>'1:16'};
DumpFile('test.yaml',$foo);

红宝石:

require('yaml')
foo=YAML.load_file('test.yaml')
puts(foo['abc'])

Ruby代码将打印4560。其中一条评论了解了如何从4560获得1:16,它是1小时,16分钟转换为秒。呃,好的。

3 个答案:

答案 0 :(得分:5)

根据Yaml 1.1 spec1:16是六十进制(60)格式的整数。

另请参阅http://yaml.org/type/int.html,其中说:

  

使用“:”允许在基数60中表示整数,这对于时间和角度值是方便的。

Ruby中包含的Yaml解析器,Psych,recognises this format and converts the value into an integer(错误地,1:16应该是71 - Psych代码似乎认为所有这些值都将采用a:b:c形式但是正则表达式不强制执行)。 Perl发射器(至少我测试过的YAML :: XS)无法识别这种格式,所以在编写文件时不引用字符串。 YAML :: XS 识别并引用一些整数,但不是全部。 YAML :: XS也不承认Psych所做的许多其他格式(例如日期)。

(似乎是性别格式has been removed from the Yaml 1.2 spec。)

Psych在解析时提供了相当大的灵活性 - YAML.load_file只是常见用例的简单接口。

你可以使用Psych的parse方法创建yaml的树表示,然后使用自定义ScalarScanner(它是转换某些字符串的对象)将其转换为Ruby数据结构格式为适当的Ruby类型):

require('yaml')

class MyScalarScanner < Psych::ScalarScanner
  def tokenize string
    #this is the same regexp as Psych uses to detect base 60 ints:
    return string if string =~ /^[-+]?[0-9][0-9_]*(:[0-5]?[0-9])+$/
    super
  end
end

tree = YAML::parse_file 'test.yaml'
foo = Psych::Visitors::ToRuby.new(MyScalarScanner.new).accept tree

这与使用YAML.load_file时的过程基本相同,只是它使用自定义扫描程序类。

类似的替代方法是打开ScalarScanner并将tokenize方法替换为自定义方法。这将允许您使用更简单的load_file接口,但有关于猴子修补类的常见警告:

class Psych::ScalarScanner
  alias :orig_tokenize :tokenize
  def tokenize string
    return string if string =~ /^[-+]?[0-9][0-9_]*(:[0-5]?[0-9])+$/
    orig_tokenize string
  end
end

foo = YAML.load_file 'test.yaml'

请注意,这些示例仅考虑格式为1:16的值。根据您的Perl程序发出的内容,您可能还需要覆盖其他模式。您可能想要查看的一个特别是性感浮动(例如1:16.44)。

答案 1 :(得分:1)

您正在使用的解析器中存在错误。似乎认为1:16是某种时间(因为4560是一小时16分钟内的秒数),但我发现没有任何证据可以证明这种解释。

最好的解决方案是使用一个没有错误的解析器。

    YAML :: XS使用的
  • libyaml,据说有Ruby绑定。
  • libsyck,由YAML :: Syck使用,据说有Ruby绑定。

另一种方法是生成YAML,其中字符串总是被引用(或者至少当它们被视为时间时)。

YAML::Syck可以选择这样做。

$ perl -e'
   use YAML::Syck qw( Dump );
   local $YAML::Syck::SingleQuote = 1;
   print(Dump({abc=>"1:16"}));
'
--- 
"abc": '1:16'

(不知道我之前是如何错过这个选项的!)

答案 2 :(得分:-4)

Ruby将所有YAML条目解释为字符串,除非它们适合a handful of special formats。条目1:16看起来像是一段时间的特殊格式,所以Ruby错误地解释了它。

您需要强制Ruby将字段解释为字符串。有两种方法可以做到这一点。以下YAML输出中的任何一个都应该为您提供所需的结果:

abc: !str 1:16
abc: '1:16'

要生成此输出,请尝试以下Perl代码:

my $foo={'abc'=>'!str 1:16'};
my $foo={'abc'=>"'1:16'"};

<强>更新 我能够使用以下代码在Perl和Ruby之间传递数据:

的Perl:

use YAML::XS qw(DumpFile);
my $foo={'abc'=>'1:16'};
DumpFile('test.yaml',$foo);

红宝石:

require 'yaml'
foo=YAML.parse_file('test.yaml')
foo['abc'].value
=> "1:16"
foo['abc'].value.class
=> String

使用的结果比load_file返回的简单哈希要复杂一些,但看起来它至少会按预期解析文件。

相关问题