【发布时间】:2020-10-28 08:07:47
【问题描述】:
首先,我对这个问题进行了搜索。我找到了 C 接口的答案和 Java 的答案。没有找到适用于 C++ 的。不幸的是,C 示例中调用的方法在 C++ API 中不存在,因此人们不能仅仅模仿特定 stackoverflow 讨论/主题中提供的答案。
我正在尝试一些应该相当简单的事情。然而,一两个小时后,我才设法接近答案,但还没有找到答案。为了简单起见,我将尝试写入的记录减少到仅 1 个字段。该字段是一个可以为空的字符串。在 Avro 中,这意味着该字段是可选的。字段的 null 方面是通过 Avro 联合完成的,其中约定是 null 值在该字段的架构中首先出现。
到目前为止,我从大量的试验和错误中学到了什么:
- 对于要写入的记录,您需要在模板化的 codec_traits 结构中使用编码器和解码器。这通常在某处的标头中定义。
- 如果我正在从文件中加载架构,那么您需要在单独的文件中以 JSON 格式定义该架构。
- 在您的 C++ 代码中,您使用您加载的模式声明一个 avro::DataFileWriter,以及来自上述标头的一条记录。然后,您将拥有一个用数据填充的本地记录,然后调用 write() 方法。
应该足够简单。然而并没有那么多。对于上述列表中的详细信息,以下是我当前使用的代码:
- 标题:
#ifndef RECURSIVE_HH
#define RECURSIVE_HH
#include "Specific.hh"
#include "Encoder.hh"
#include "Decoder.hh"
namespace recursive_record
{
struct recursive_data
{
std::string fstring;
};
}
namespace avro
{
template<> struct codec_traits<recursive_record::recursive_data>
{
static void encode( Encoder& e, const recursive_record::recursive_data& v )
{
avro::encode( e, v.fstring );
}
static void decode( Decoder& d, recursive_record::recursive_data& v )
{
avro::decode( d, v.fstring );
}
};
}
#endif /* RECURSIVE_HH */
- JSON 架构文件:
{
"type": "record",
"name": "Root",
"fields": [
{
"name": "fstring",
"type": [
"null",
"string"
]
}
]
}
- 主要的 C++ 文件(请注意,出于简洁的原因,我已截断该文件,因此以下代码中没有使用(或者更确切地说是看到)一些包含的标头:
#include "recursive.h"
#include "Encoder.hh"
#include "Decoder.hh"
#include "Generic.hh"
#include "GenericDatum.hh"
#include "ValidSchema.hh"
#include "DataFile.hh"
#include "Types.hh"
#include "Compiler.hh"
#include "Stream.hh"
avro::ValidSchema loadSchema(const char* filename)
{
std::ifstream ifs(filename);
avro::ValidSchema result;
avro::compileJsonSchema(ifs, result);
return result;
}
int main( int argc, char** argv )
{
/**********************************************************************************
AVRO WRITER EXAMPLE
**********************************************************************************/
try
{
//Filename definitions skipped for brevity
avro::ValidSchema recursiveSchema = loadSchema( schemaFilename );
avro::DataFileWriter<recursive_record::recursive_data> dfw( filename, recursiveSchema );
recursive_record::recursive_data record;
record.fstring = std::string("First string");
dfw.write( record );
dfw.close();
}
catch( const std::exception& e )
{
// Log a message
return -1;
}
}
“那么问题是什么?”你可能会问。好吧,文件实际上已经成功写入,至少代码没有崩溃并且生成了一个 Avro 数据文件。到目前为止,一切都很好。但是,如果您尝试读取该文件,则会收到以下错误:
AVRO read error: vector::_M_range_check: __n (which is 12) >= this->size() (which is 2)
什么-???是的。 '整个下午都在做这个。
经过大量实验后,我发现问题出在给定字段的可空方面。我还注意到,如果我从架构中删除了可为空的选项,那么架构就变成了这样:
{
"type": "record",
"name": "Root",
"fields": [
{
"name": "fstring",
"type": "string"
}
]
}
而我没有改变,那么新的Avro数据文件不仅写入成功,而且读取也成功,因此:
[rh6lgn01][1881] MY_EXAMPLES/generate_recursive$ recursive
schema=recursive.json
file=./DATA/recursive.avro
recursiveSchema valid = true
ReadFile(): Type = record
ProcessRecord(): New record found. Field count = 1
ProcessRecord(): {
ProcessRecord(): Field 0: type = string
ProcessDatum(): Field 0: value = First string (length= 12)
ProcessRecord(): }
rowCount = 1
AVRO Writing and Reading Complete
[rh6lgn01][1882] MY_EXAMPLES/generate_recursive$
当我阅读 Java 问题时,我有一些希望。有一个答案指出 - 在 Java 中 - 有一个 @Nullable 标记,您可以将其与记录中的字段相关联。这是该问题的链接: Storing null values in avro files
C++ 语言中当然没有这样的机制。我确实在 Types.hh 标头中找到了以下似乎相关的代码行:
/// define a type to identify Null in template functions
struct AVRO_DECL Null { };
但是,我不知道如何以类似的方式使用它。所以我要么错过了一些东西,要么它有不同的目的。我害怕前者,但怀疑后者。
这是一个指向 stackoverflow C 问题的链接及其答案,以供完成: Write nullable item to avro record in Avro C
我正在使用 1.9.2 版的 Avro C++ 库,在 GNU/Linux 机器上运行(这无关紧要,但为了完成)。
我将继续推动并寻求答案,但如果有人以前这样做过并且可以提供一些启发,我将不胜感激。
谢谢!
【问题讨论】:
-
“一两个小时后” “大量的反复试验”。嗯。我猜这些天“相当大”一定意味着别的东西;)
-
听起来你需要生成一个minimal reproducible example 然后询问 Avro 开发人员。