如何修复预期的启动工会。在命令行上将 JSON 转换为 Avro 时得到 VALUE_NUMBER_INT?

Posted

技术标签:

【中文标题】如何修复预期的启动工会。在命令行上将 JSON 转换为 Avro 时得到 VALUE_NUMBER_INT?【英文标题】:How to fix Expected start-union. Got VALUE_NUMBER_INT when converting JSON to Avro on the command line? 【发布时间】:2015-02-13 15:21:06 【问题描述】:

我正在尝试使用 Avro 模式验证 JSON 文件并编写相应的 Avro 文件。首先,我定义了以下名为 user.avsc 的 Avro 架构:

"namespace": "example.avro",
 "type": "record",
 "name": "user",
 "fields": [
     "name": "name", "type": "string",
     "name": "favorite_number",  "type": ["int", "null"],
     "name": "favorite_color", "type": ["string", "null"]
 ]

然后创建了一个user.json 文件:

"name": "Alyssa", "favorite_number": 256, "favorite_color": null

然后尝试运行:

java -jar ~/bin/avro-tools-1.7.7.jar fromjson --schema-file user.avsc user.json > user.avro

但我得到以下异常:

Exception in thread "main" org.apache.avro.AvroTypeException: Expected start-union. Got VALUE_NUMBER_INT
    at org.apache.avro.io.JsonDecoder.error(JsonDecoder.java:697)
    at org.apache.avro.io.JsonDecoder.readIndex(JsonDecoder.java:441)
    at org.apache.avro.io.ResolvingDecoder.doAction(ResolvingDecoder.java:290)
    at org.apache.avro.io.parsing.Parser.advance(Parser.java:88)
    at org.apache.avro.io.ResolvingDecoder.readIndex(ResolvingDecoder.java:267)
    at org.apache.avro.generic.GenericDatumReader.read(GenericDatumReader.java:155)
    at org.apache.avro.generic.GenericDatumReader.readField(GenericDatumReader.java:193)
    at org.apache.avro.generic.GenericDatumReader.readRecord(GenericDatumReader.java:183)
    at org.apache.avro.generic.GenericDatumReader.read(GenericDatumReader.java:151)
    at org.apache.avro.generic.GenericDatumReader.read(GenericDatumReader.java:142)
    at org.apache.avro.tool.DataFileWriteTool.run(DataFileWriteTool.java:99)
    at org.apache.avro.tool.Main.run(Main.java:84)
    at org.apache.avro.tool.Main.main(Main.java:73)

我错过了什么吗?为什么我会收到“Expected start-union. Got VALUE_NUMBER_INT”。

【问题讨论】:

我们遇到了同样的问题,现在正在使用avro-json-decoder,zolyfarkas' org.apache.avro.io.ExtendedJsonDecoder 的独立版本和following patch。 【参考方案1】:

根据the explanation by Doug Cutting,

Avro 的 JSON 编码要求标记非空联合值 与他们的预期类型。这是因为工会喜欢 ["bytes","string"] 和 ["int","long"] 在 JSON 中不明确,第一个 都编码为 JSON 字符串,而第二个都编码为 JSON 数字。

http://avro.apache.org/docs/current/spec.html#json_encoding

因此您的记录必须编码为:

"name": "Alyssa", "favorite_number": "int": 7, "favorite_color": null

【讨论】:

感谢您的见解。期待@ppearcy 提到的 AVRO-1582【参考方案2】:

有一个新的 JSON 编码器正在开发中,可以解决这个常见问题:

https://issues.apache.org/jira/browse/AVRO-1582

https://github.com/zolyfarkas/avro

【讨论】:

太棒了! :) 感谢您的告知。【参考方案3】:

正如@Emre-Sevinc 所指出的,问题在于 Avro 记录的编码。

在这里更具体;

不要这样做:

   jsonRecord = avroGenericRecord.toString

相反,这样做:

    val writer = new GenericDatumWriter[GenericRecord](avroSchema)
    val baos = new ByteArrayOutputStream
    val jsonEncoder = EncoderFactory.get.jsonEncoder(avroSchema, baos)
    writer.write(avroGenericRecord, jsonEncoder)
    jsonEncoder.flush

    val jsonRecord = baos.toString("UTF-8")

您还需要以下导入:

import org.apache.avro.Schema
import org.apache.avro.generic.GenericData, GenericDatumReader, GenericDatumWriter, GenericRecord
import org.apache.avro.io.DecoderFactory, EncoderFactory

执行此操作后,您将获得jsonRecord,其中包含带有预期类型标记的非空联合值。

希望这会有所帮助!

【讨论】:

【参考方案4】:

我已经实现了 union 及其验证,只需创建一个 union 模式并通过 postman 传递它的值。 resgistry url 是您为 kafka 的属性指定的 url,您也可以将动态值传递给您的架构

RestTemplate template = new RestTemplate();
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<String> entity = new HttpEntity<String>(headers);
        ResponseEntity<String> response = template.exchange(""+registryUrl+"/subjects/"+topic+"/versions/"+version+"", HttpMethod.GET, entity, String.class);
        String responseData = response.getBody();
        JSONObject jsonObject = new JSONObject(responseData);
        JSONObject jsonObjectResult = new JSONObject(jsonResult);
        String getData = jsonObject.get("schema").toString();
        Schema.Parser parser = new Schema.Parser();
        Schema schema = parser.parse(getData);
        GenericRecord genericRecord = new GenericData.Record(schema);
        schema.getFields().stream().forEach(field->
            genericRecord.put(field.name(),jsonObjectResult.get(field.name()));
        );
        GenericDatumReader<GenericRecord>reader = new GenericDatumReader<GenericRecord>(schema);
        boolean data = reader.getData().validate(schema,genericRecord );

【讨论】:

注意:schema-registry-client 可以作为依赖项包含在调用getSchemaByVersion

以上是关于如何修复预期的启动工会。在命令行上将 JSON 转换为 Avro 时得到 VALUE_NUMBER_INT?的主要内容,如果未能解决你的问题,请参考以下文章

在命令行上将 XLS 转换为 CSV

如何修复预期的字符串,但在带有改造的嵌套数组上是 BEGIN_OBJECT

raspbian 如何启动系统修复

我将如何修复这些“ORA-00933:SQL 命令未正确结束”“ORA-00923:未在预期位置找到 FROM 关键字”错误?

如何在 Swiftui 上将“ImagePickerView”类型的值转换为预期的参数类型“String”?

bcdboot(引导修复工具) 命令行工具使用方法