我在使用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分钟转换为秒。呃,好的。
答案 0 :(得分:5)
根据Yaml 1.1 spec,1: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分钟内的秒数),但我发现没有任何证据可以证明这种解释。
最好的解决方案是使用一个没有错误的解析器。
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
返回的简单哈希要复杂一些,但看起来它至少会按预期解析文件。