avro::jsonEncoder编码Union类型数据抛出异常的解决办法
问题描述
我在data_avro.json文件中定义了如下AVRO Schema:
[ { "namespace": "my.data", "type": "record", "name": "Data1", "fields" : [ {"name": "i", "type": "int"} ] }, { "namespace": "my.data", "type": "record", "name": "Data2", "fields" : [ {"name": "u", "type": "double"}, {"name": "val", "type": "int"} ] } ]
尝试通过以下代码进行数据编码:
avrodata::Data1 data1; data1.i = 1; avrodata::Data2 data2; data2.val = 0; data2.u = 2.5; std::ifstream ifs("data_avro.json"); avro::ValidSchema schemas; std::string err; if (!avro::compileJsonSchema(ifs, schemas, err)) std::cerr << "compileJsonSchema error : " << err << std::endl; else std::cout << "compileJsonSchema success" << std::endl; auto encoder = avro::jsonEncoder(schemas); auto out = avro::memoryOutputStream(); encoder->init(*out); avro::encode(*encoder, data1); // 此处抛出avro::Exception异常
运行后出现错误,输出如下:
compileJsonSchema success terminate called after throwing an instance of 'avro::Exception' what(): Invalid operation. Expected: Int got Union
使用avro::binaryEncoder时可以正常工作,但avro::jsonEncoder不行,请问如何使用avro::jsonEncoder完成该数据的编码?
解决方案
问题原因
你传入的Schema是多个Record组成的数组,编译后会被解析成Union类型(即[Data1, Data2]的联合类型),而非两个独立的Schema。binaryEncoder对Union类型的处理较为宽松,但jsonEncoder要求必须明确指定要编码的是Union中的哪一个分支类型,不能直接传入具体的Record对象。
解决步骤
要正确使用jsonEncoder编码Union类型的数据,需通过avro::GenericDatum包装具体数据,并指定对应的Schema分支:
- 从编译后的Union Schema中获取对应Record的子Schema
- 创建
GenericDatum并写入具体数据,指定Union分支索引 - 对
GenericDatum执行编码操作
完整修改后的代码
avrodata::Data1 data1; data1.i = 1; avrodata::Data2 data2; data2.val = 0; data2.u = 2.5; std::ifstream ifs("data_avro.json"); avro::ValidSchema schemas; std::string err; if (!avro::compileJsonSchema(ifs, schemas, err)) std::cerr << "compileJsonSchema error : " << err << std::endl; else std::cout << "compileJsonSchema success" << std::endl; auto encoder = avro::jsonEncoder(schemas); auto out = avro::memoryOutputStream(); encoder->init(*out); // 处理Data1的编码:指定Union分支0 const avro::ValidSchema& data1Schema = schemas.unionBranch(0); avro::GenericDatum datum(schemas); datum.setUnionIndex(0); avro::GenericDatum recordDatum(data1Schema); recordDatum.field("i") = data1.i; datum.unionDatum() = recordDatum; avro::encode(*encoder, datum); // 完成编码并输出JSON结果 encoder->flush(); auto buffer = avro::memoryOutputStream::cast(*out)->buffer(); std::string jsonStr(buffer->begin(), buffer->end()); std::cout << jsonStr << std::endl;
额外说明
如果你的需求是分别编码两个独立的Record而非Union类型,建议修改data_avro.json,每次仅传入单个Record的Schema;或在代码中分别编译两个Schema,而非一次性编译整个数组。
内容的提问来源于stack exchange,提问作者vazlsky
相关产品推荐
相关产品推荐

