如果我使用模式版本1序列化一个对象,然后将模式更新为版本2(比如添加一个字段),那么在以后反序列化该对象时是否需要使用模式版本2?理想情况下,我只希望使用模式版本2,并使反序列化对象具有在对象最初序列化后添加到模式中的字段的默认值。
也许一些代码会更好地解释...
架构 1:
{"type": "record",
"name": "User",
"fields": [
{"name": "firstName", "type": "string"}
]}
方案2:
{"type": "record",
"name": "User",
"fields": [
{"name": "firstName", "type": "string"},
{"name": "lastName", "type": "string", "default": ""}
]}
使用通用非代码生成方法:
// serialize
ByteArrayOutputStream out = new ByteArrayOutputStream();
Encoder encoder = EncoderFactory.get().binaryEncoder(out, null);
GenericDatumWriter writer = new GenericDatumWriter(schema1);
GenericRecord datum = new GenericData.Record(schema1);
datum.put("firstName", "Jack");
writer.write(datum, encoder);
encoder.flush();
out.close();
byte[] bytes = out.toByteArray();
// deserialize
// I would like to not have any reference to schema1 below here
DatumReader<GenericRecord> reader = new GenericDatumReader<GenericRecord>(schema2);
Decoder decoder = DecoderFactory.get().binaryDecoder(bytes, null);
GenericRecord result = reader.read(null, decoder);
导致EOFException。使用jsonEncoder
会导致AvroTypeException。
我知道如果我将schema1和schema2都传递给GenericDatumReader
构造函数,它将起作用,但是我希望不必保留所有先前模式的存储库,并且还以某种方式跟踪用于序列化每个特定对象的模式。
我还尝试了code-gen方法,首先使用Schema1生成的User类序列化为文件:
User user = new User();
user.setFirstName("Jack");
DatumWriter<User> writer = new SpecificDatumWriter<User>(User.class);
FileOutputStream out = new FileOutputStream("user.avro");
Encoder encoder = EncoderFactory.get().binaryEncoder(out, null);
writer.write(user, encoder);
encoder.flush();
out.close();
然后将模式更新到版本2,重新生成用户类,并尝试读取文件:
DatumReader<User> reader = new SpecificDatumReader<User>(User.class);
FileInputStream in = new FileInputStream("user.avro");
Decoder decoder = DecoderFactory.get().binaryDecoder(in, null);
User user = reader.read(null, decoder);
但它也会导致EOFEException。
只是为了比较,我试图做的事情似乎与protobufs一起工作......
格式:
option java_outer_classname = "UserProto";
message User {
optional string first_name = 1;
}
序列 化:
UserProto.User.Builder user = UserProto.User.newBuilder();
user.setFirstName("Jack");
FileOutputStream out = new FileOutputStream("user.data");
user.build().writeTo(out);
将可选的last_name添加到格式化、重新生成UserProto和反序列化:
FileInputStream in = new FileInputStream("user.data");
UserProto.User user = UserProto.User.parseFrom(in);
如预期,<code>用户。getLastName()是空字符串。
这样的事情可以用Avro来做吗?
我试图回避这个问题。我把它放在这里:
我还尝试使用两个模式一个模式只是使用Avro的反射API向另一个模式添加另一列。我有以下模式:
Employee (having name, age, ssn)
ExtendedEmployee (extending Employee and having gender column)
我假设先前有<code>Employee
RecordHandler rh = new RecordHandler();
if (rh.readObject(employeeSchema, dbLocation) instanceof Employee) {
Employee e = (Employee) rh.readObject(employeeSchema, dbLocation);
System.out.print(e.toString());
} else if (rh.readObject(schema, dbLocation) instanceof ExtendedEmployee) {
ExtendedEmployee e = (ExtendedEmployee) rh.readObject(schema, dbLocation);
System.out.print(e.toString());
}
这解决了这里的问题。但是,我很想知道是否有一个API,我们可以使用<code>ExtendedEmployee
要执行您正在尝试执行的操作,您需要通过允许空值使last_name字段成为可选的。last_name的类型应该是["null","string"]而不是"string"
Avro和协议缓冲区有不同的方法来处理版本控制,哪种方法更好取决于您的用例。
在协议缓冲区中,您必须用数字显式标记每个字段,这些数字与字段的值一起存储在二进制表示中。因此,只要在后续模式版本中从不更改数字的含义,您仍然可以解码在不同模式版本中编码的记录。如果解码器看到它无法识别的标签号,它可以简单地跳过它。
Avro采用了不同的方法:没有标签号,相反,二进制布局完全由编码程序决定-这是作者的模式。(记录的字段只是以二进制编码的形式一个接一个地存储,没有任何标记或分隔符,顺序由编写者的模式决定。)这使得编码更加紧凑,并且不必在模式中手动维护标记。但这确实意味着,为了阅读,您必须知道写入数据的确切模式,否则您将无法理解它。
如果了解写入器的模式对于解码Avro至关重要,那么读取器的模式就是它之上的一层美好。如果您在需要读取Avro数据的程序中进行代码生成,您可以根据读取器的模式进行编码,这样您就不必在每次写入器的模式更改时重新生成它(假设它以可以解决的方式更改)。但这并不能使您不必了解写入器的模式。
Avro的方法在一个环境中很有用,因为你有很多已知具有完全相同模式版本的记录,因为你可以在文件开头的元数据中包含模式,并且知道接下来的一百万条记录都可以使用该模式解码。这在MapReduce上下文中经常发生,这解释了为什么Avro来自Hadoop项目。
Protocol Buffers的方法可能更适合RPC,其中通过网络发送单个对象(作为请求参数或返回值)。如果您在此处使用Avro,您可能有不同的客户端和不同的服务器,它们都具有不同的模式版本,因此您必须使用它使用的Avro模式版本标记每个二进制编码的blob,并维护模式注册表。此时,您还不如使用Protocol Buffers的内置标记。
我是Hadoop和编程的新手,我对Avro模式演变有点困惑。我将解释到目前为止我对Avro的理解。 Avro是一种串行化工具,它存储二进制数据,其json模式位于顶部。模式如下所示。 现在我的问题是为什么我们需要进化?我已经了解到,我们可以在新字段的模式中使用<code>default</code>选项;但是,如果我们在文件中添加一个新的模式,早期的模式将被覆盖。一个文件不能有两个架构。 另一个问
我有两个问题: > 我曾尝试使用模式V1编写记录,并使用模式V2读取记录,但出现以下错误: org.apache.avro。AvroTypeException:找到foo,应为foo 我使用avro-1.7.3和: 以下是这两种模式的示例(我也尝试过添加命名空间,但没有成功)。 架构V1: 架构V2: 提前谢谢。
我正试图了解更多关于我们在Kafka主题中使用的Avro模式的信息,我对这一点相对来说比较陌生。 我想知道是否有一种方法可以在特定情况下发展模式。我们用一个不能为null的新字段或任何默认值来更新模式,因为这些新字段是标识符。解决这个问题的方法是创建新主题,但是有没有更好的方法来改进现有模式?
我有一个简单的案例类: 我正在添加字段“name” java.util.NoSuchelementException:scala.collection.immutable.stream$empt$.head(stream.scala:1104)在scala.collection.immutable.stream$empt$.head(stream.scala:1102)在test.consumer
我尝试使用avro-python3(向后兼容性)重新创建一个模式演变案例。 我有两个模式: 第二个模式没有字段,但有两个附加字段:和。 根据avro模式演化规则,如果我用schema_v1写入avro记录: …我可以使用schema_v2读取它,前提是不存在字段有默认值 但我得到了以下错误: 我知道这在Java中有效。这是一个视频课程的示例。有没有办法让它在python中工作?
当我试图用一个简单的Java程序测试Avro模式演化时,我得到了一个< code>ClassCastException。 Avro版本: 你能让我知道如何解决这个错误吗?