如何使用 Apache Avro 的 GenericRecord 处理动态数据?

5

我想使用Apache Avro来序列化我的数据,我的客户端是用C++编写的,而我的服务器是用Java编写的。

  1. My server java code looks like this:

    Schema scm = new Schema.Parser().parse("....shcema String.....");
    ByteArrayInputStream inputStream = new ByteArrayInputStream(record.array());
    Decoder coder = new DecoderFactory().directBinaryDecoder(inputStream, null);
    GenericDatumReader<GenericRecord> reDatumReader = new GenericDatumReader<GenericRecord>(scm);
    try {
        GenericRecord result = (GenericRecord)reDatumReader.read(null, coder);
                //here! the result "name", "num_groups" is empty!
        System.out.println(result.get("name")+"   "+result.get("num_groups"));
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } 
    
  2. And my client code:

    std::string schemaDescript ="....shcema String.....";
    
    std::stringstream rsStream(schemaDescript);
    avro::ValidSchema rSchema;
    avro::compileJsonSchema(rsStream, rSchema);
    avro::EncoderPtr encoder = avro::binaryEncoder();
    std::auto_ptr<avro::OutputStream> oStream = avro::memoryOutputStream();
    encoder->init(*oStream);   
    avro::GenericDatum rData(rSchema);
    avro::GenericRecord sReord = rData.value<avro::GenericRecord>();
    sReord.setFieldAt(0, avro::GenericDatum("i am nice"));
    sReord.setFieldAt(1, avro::GenericDatum(1));
    sReord.setFieldAt(2, avro::GenericDatum(12));
    sReord.setFieldAt(3, avro::GenericDatum(13));
    
    avro::GenericWriter gwriter(rSchema, encoder);
    gwriter.write(rData);
    oStream->flush();
    
    std::auto_ptr<avro::InputStream> inSt = avro::memoryInputStream(*oStream);
    avro::StreamReader instReader(*inSt);
    
    size_t outputLen = oStream->byteCount();
    uint8_t* theByteData = new uint8_t[outputLen];
    instReader.hasMore();
    instReader.readBytes(theByteData, outputLen);
    
我将theByteData发送到服务器,代码正常运行(没有异常),但结果为空,有人可以告诉我出了什么问题吗?
并且在Java中,我们使用键获取值:result.get("name");但在C++中,我们使用索引获取值:record.fieldAt(0).value<string>()。如果我无法使用字符串键获取值,如何将索引匹配到字符串键?

感谢dominikh,编辑了我的问题。 - user1833610
1
三年后,有什么新消息吗? - Jérôme B
2个回答

1
我今天早上遇到了同样的问题,并在Avro测试Cpp文件(“DataFileTests.cc”)中的“testWriteGeneric”函数中找到了解决方案。
例如:
我的模式文件(cpx.json):
{
  "type": "record",
  "name": "cpx",
  "fields" : [
  {"name": "re", "type": "double"},
  {"name": "im", "type" : "int"}
  ]
 }

我的Cpp文件:
typedef std::pair<avro::ValidSchema, avro::GenericDatum> Pair;

int main(int ac, char **av)
{

    // encode
    std::ifstream ifs(cpx.json);
    avro::ValidSchema schema;
    avro::compileJsonSchema(ifs, schema);

    // I create a pair of validSchema and GenericDatum
    Pair p(schema, avro::GenericDatum());

    avro::GenericDatum &Data = p.second;
    Data = avro::GenericDatum(schema);
    avro::GenericRecord &sReord = Data.value<avro::GenericRecord>();

    // I set my values
    sReord.setFieldAt(sReord.fieldIndex("re"), avro::GenericDatum(42.5));
    sReord.setFieldAt(sReord.fieldIndex("im"), avro::GenericDatum(24));


    // I create a DataFileWriter and i write my pair of ValidSchema and GenericValue
    avro::DataFileWriter<Pair> dataFileWriter("test.bin", schema);
    dataFileWriter.write(p);
    dataFileWriter.close();
}

0
以下陈述中的客户端代码存在两个问题。
avro::GenericRecord sReord = rData.value<avro::GenericRecord>();
sReord.setFieldAt(0, avro::GenericDatum("i am nice"));

第二个语句将导致调用avro::GenericDatum(bool)而不是预期的GenericDatum(const std::string&)。 因此,字符串字段保持为空,因此当您尝试读取它时,将返回空字符串。 因此,将上述语句替换为以下语句应该可以解决问题

std::string s("i am nice");
sReord.setFieldAt(0, avro::GenericDatum(s));

在第一条语句中,sRecord 应该被声明为引用,因为它是由 rData.value() 返回的。如果不将其作为引用进行处理,就会用一个新的副本替换它,因此在其中写入的任何值实际上都不会被写入底层流中。因此,应该这样写:
avro::GenericRecord& sReord = rData.value<avro::GenericRecord>();

此外,您不需要使用GenericWriter,可以直接使用Encoder对象进行编写。
avro::encode(*encoder, rData);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接