Ruby Avro序列化JSON遇阻:Record验证失败及Map类型调用异常
解决Ruby Avro序列化中的数据结构不匹配问题
先直接点出你踩的核心坑:你把Ruby哈希转成了JSON字符串传给dw.write,但Avro的DatumWriter需要的是原生Ruby数据结构(哈希、数组等),不是序列化后的JSON字符串!这就是两个错误的根源。
1. 修复Record Schema的序列化问题
你的第一个代码块里,message = {'field_one' => 'why', ...}.to_json把哈希转成了JSON字符串,但Avro期望的是和schema匹配的Ruby哈希。去掉.to_json即可:
# 定义schema(不用转成JSON字符串,直接传Ruby哈希也可以,Avro::Schema.parse能处理) schema = { 'type' => 'record', 'name' => 'test', 'fields' => [ { 'name' => 'field_one', 'type' => 'string' }, { 'name' => 'field_two', 'type' => 'string'}, { 'name' => 'field_three', 'type' => 'string'}, { 'name' => 'field_four', 'type' => 'string' } ] } # 注意:这里不要转成JSON字符串!直接用Ruby哈希 message = {'field_one' => 'why', 'field_two' => 'this', 'field_three' => 'no', 'field_four' => 'worky?'} schema = Avro::Schema.parse(schema.to_json) # 或者直接传schema哈希,parse也支持 dw = Avro::IO::DatumWriter.new(schema) buffer = StringIO.new() encoder = Avro::IO::BinaryEncoder.new(buffer) dw.write(message, encoder) binary_data = buffer.read
这样就不会再报The datum ... is not an example of schema ...的错误了,因为现在传给writer的是符合schema结构的Ruby哈希,不是字符串。
2. 修复Map Schema的序列化问题
同样的道理,你之前把message转成了JSON字符串,导致Avro试图对字符串调用keys方法(因为map类型需要遍历键值对),所以报错undefined method 'keys' for StringObject。修正代码:
schema = { 'type' => 'map', 'values' => 'string' } message = {'field_one' => 'why', 'field_two' => 'this', 'field_three' => 'no', 'field_four' => 'worky?'} # 原生哈希,不要转JSON schema = Avro::Schema.parse(schema.to_json) dw = Avro::IO::DatumWriter.new(schema) buffer = StringIO.new() encoder = Avro::IO::BinaryEncoder.new(buffer) dw.write(message, encoder) binary_data = buffer.read
现在writer能正确识别这是一个Ruby哈希,会按照Avro的map类型规则序列化。
3. 关于Avro二进制数据转JSON格式的需求
你提到要把序列化后的内容放到application/json的HTTP请求里,Avro默认生成的二进制数据确实不适合直接放入JSON请求。这里有两个解决方案:
- 方案一:用Avro的JSON编码器替代二进制编码器
Ruby的Avro库提供了Avro::IO::JsonEncoder,可以直接把数据序列化成JSON格式的Avro数据,这样就能直接放到HTTP请求的JSON体里:
schema = { 'type' => 'map', 'values' => 'string' } message = {'field_one' => 'why', 'field_two' => 'this'} schema = Avro::Schema.parse(schema.to_json) dw = Avro::IO::DatumWriter.new(schema) buffer = StringIO.new() encoder = Avro::IO::JsonEncoder.new(buffer, schema) # 使用JsonEncoder dw.write(message, encoder) json_avro_data = buffer.read # 现在json_avro_data是符合Avro JSON编码规范的字符串,可以直接放入application/json请求
- 方案二:把二进制Avro数据编码成Base64字符串
如果必须保留Avro二进制格式,可以把二进制数据转成Base64编码的字符串,然后作为JSON的一个字段传递:
require 'base64' # 先按之前的方式生成binary_data base64_data = Base64.strict_encode64(binary_data) # 然后把base64_data放到JSON结构里 http_body = { avro_data: base64_data }.to_json # 这样就能作为application/json的请求体发送了
最后补充一句:Ruby Avro的文档确实不够友好,核心要记住的是DatumWriter/Reader操作的是原生Ruby数据结构,不是JSON字符串,这是很多新手容易踩的坑。
内容的提问来源于stack exchange,提问作者user8897013
相关产品推荐
相关产品推荐

