You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 07:12:57