根据 Avro docs 中“default”属性的定义:“此字段的默认值,在读取缺少此字段的实例时使用(可选)。”
这意味着如果缺少相应字段,则采用默认值。
但事实似乎并非如此。考虑以下
student
模式:
{
"type": "record",
"namespace": "com.example",
"name": "Student",
"fields": [{
"name": "age",
"type": "int",
"default": -1
},
{
"name": "name",
"type": "string",
"default": "null"
}
]
}
Schema 表示:如果缺少“age”字段,则将值视为 -1。对于“名称”字段也是如此。
现在,如果我尝试从以下 JSON 构建学生模型:
{"age":70}
我得到这个例外:
org.apache.avro.AvroTypeException: Expected string. Got END_OBJECT
at org.apache.avro.io.JsonDecoder.error(JsonDecoder.java:698)
at org.apache.avro.io.JsonDecoder.readString(JsonDecoder.java:227)
看起来默认设置没有按预期工作。那么,默认在这里到底扮演什么角色呢?
这是用于生成学生模型的代码:
Decoder decoder = DecoderFactory.get().jsonDecoder(Student.SCHEMA$, studentJson);
SpecificDatumReader<Student> datumReader = new SpecificDatumReader<>(Student.class);
return datumReader.read(null, decoder);
(
Student
类由 Avro 编译器根据学生模式自动生成)
我认为对默认值存在一些误解,因此希望我的解释对其他人也有帮助。当字段不存在时,默认值对于给出默认值很有用,但这本质上是当您实例化 avro 对象时(在您的情况下调用
datumReader.read
),但它不允许使用不同的模式读取数据,这这就是为什么“模式注册表”的概念对于这种情况很有用。
以下代码有效并允许读取您的数据
Decoder decoder = DecoderFactory.get().jsonDecoder(Student.SCHEMA$, "{\"age\":70}");
SpecificDatumReader<Student> datumReader = new SpecificDatumReader<>(Student.class);
Schema expected = new Schema.Parser().parse("{\n" +
" \"type\": \"record\",\n" +
" \"namespace\": \"com.example\",\n" +
" \"name\": \"Student\",\n" +
" \"fields\": [{\n" +
" \"name\": \"age\",\n" +
" \"type\": \"int\",\n" +
" \"default\": -1\n" +
" }\n" +
" ]\n" +
"}");
datumReader.setSchema(expected);
System.out.println(datumReader.read(null, decoder));
如您所见,我指定了用于“写入”json 输入的架构,该输入不包含“名称”字段,但是(考虑到您的架构包含默认值)当您打印记录时,您将看到名称你的默认值
{"age": 70, "name": "null"}
以防万一,可能知道也可能不知道,“null”并不是真正的空值,而是值为“null”的字符串。
只是补充上面答案中已经说过的内容。为了使字段在不存在时为空。然后将其类型与 null 联合。否则它只是一个拼写为 null 的字符串。示例架构:
{
"name": "name",
"type": [
"null",
"string"
],
"default": null
}
然后如果您添加
{"age":70}
并检索记录,您将得到以下结果:
{"age":70,"name":null}
default
字段供读者使用。作者必须提供所有字段。