Как использовать Apache Avro GenericRecord для динамических данных?

Я хочу использовать Apache Avro для сериализации моих данных, мой клиент написан на C ++, а мой сервер написан на Java.

  1. Мой серверный Java-код выглядит так:

    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. И мой код клиента:

    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>(), Если я не могу получить значение с помощью строкового ключа, как сопоставить индекс с строковым ключом?

5

Решение

У меня была та же проблема этим утром, и я нашел решение в файле Cpp Avro Test («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();
}
1

Другие решения

В следующих утверждениях есть 2 проблемы с клиентским кодом

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);
0