首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >用读取器模式将Avro文件转换为JSON

用读取器模式将Avro文件转换为JSON
EN

Stack Overflow用户
提问于 2017-12-24 18:40:04
回答 2查看 34.8K关注 0票数 3

我想用与编写器模式不同的读取器模式反序列化命令行上的Avro数据。我可以在序列化时指定编写器架构,但不能在反序列化期间指定。

record.json (数据文件):

代码语言:javascript
复制
{"test1": 1, "test2": 2}

writer.avsc (编写模式):

代码语言:javascript
复制
{
    "type": "record",
    "name": "pouac",
    "fields": [
        {
            "name": "test1",
            "type": "int"
        },
        {
            "name": "test2",
            "type": "int"
        }
    ]
}

reader.avsc (读者模式):

代码语言:javascript
复制
{
    "type": "record",
    "name": "pouac",
    "fields": [{
        "name": "test2",
         "type": "int",
         "aliases": ["test1"]
    }]
}

序列化数据:

代码语言:javascript
复制
$ java -jar avro-tools-1.8.2.jar fromjson --schema-file writer.avsc record.json > record.avro

对于反序列化数据,我尝试了以下方法:

代码语言:javascript
复制
$ java -jar avro-tools-1.8.2.jar tojson --schema-file reader.avsc record.avro
Exception in thread "main" joptsimple.UnrecognizedOptionException: 'schema-file' is not a recognized option
...

我主要是在寻找命令行指令,因为我不太适合编写Java代码,但是我很乐意使用Java代码来编译自己。实际上,我感兴趣的是确切的反序列化结果。(更根本的问题是在这次谈话中描述的,我打开fastavro PR是为了实现别名)

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2017-12-26 19:20:41

avro-tools tojson目标仅用作将二进制编码的Avro文件转换为JSON的转储工具。模式总是伴随着Avro文件中的记录,如下面的链接所示。因此,它不能被avro-tools覆盖。

http://avro.apache.org/docs/1.8.2/#compare

我不知道一个独立的工具,可以用来实现你想要的。我认为您需要进行一些编程才能达到预期的结果。Avro有许多支持的语言,包括Python,但是跨语言的功能并不统一。在我的经验中,Java是最先进的。例如,Python缺乏在DataFileReader上指定读取器模式的能力,这将有助于实现您想要的结果:

https://github.com/apache/avro/blob/master/lang/py/src/avro/datafile.py#L224

在Python中最接近的是以下内容;

代码语言:javascript
复制
import avro.schema as avsc
import avro.datafile as avdf
import avro.io as avio

reader_schema = avsc.parse(open("reader.avsc", "rb").read())

# need ability to inject reader schema as 3rd arg
with avdf.DataFileReader(open("record.avro", "rb"), avio.DatumReader()) as reader:
    for record in reader:
        print record

在模式和您所概述的数据方面。预期的行为应该是未定义的,因此会发出错误

这种行为可以通过以下Java代码进行验证;

代码语言:javascript
复制
package ca.junctionbox.soavro;

import org.apache.avro.Schema;
import org.apache.avro.SchemaValidationException;
import org.apache.avro.SchemaValidationStrategy;
import org.apache.avro.SchemaValidator;
import org.apache.avro.SchemaValidatorBuilder;

import java.util.ArrayList;

public class Main {
    public static final String V1 = "{\n" +
            "    \"type\": \"record\",\n" +
            "    \"name\": \"pouac\",\n" +
            "    \"fields\": [\n" +
            "        {\n" +
            "            \"name\": \"test1\",\n" +
            "            \"type\": \"int\"\n" +
            "        },\n" +
            "        {\n" +
            "            \"name\": \"test2\",\n" +
            "            \"type\": \"int\"\n" +
            "        }\n" +
            "    ]\n" +
            "}";

    public static final String V2 = "{\n" +
            "    \"type\": \"record\",\n" +
            "    \"name\": \"pouac\",\n" +
            "    \"fields\": [{\n" +
            "        \"name\": \"test2\",\n" +
            "         \"type\": \"int\",\n" +
            "         \"aliases\": [\"test1\"]\n" +
            "    }]\n" +
            "}";

    public static void main(final String[] args) {
        final SchemaValidator sv = new SchemaValidatorBuilder()
                .canBeReadStrategy()
                .validateAll();
        final Schema sv1 = new Schema.Parser().parse(V1);
        final Schema sv2 = new Schema.Parser().parse(V2);
        final ArrayList<Schema> existing = new ArrayList<>();
        existing.add(sv1);

        try {
            sv.validate(sv2, existing);
            System.out.println("Good to go!");
        } catch (SchemaValidationException e) {
            e.printStackTrace();
        }
    }
}

这将产生以下输出:

代码语言:javascript
复制
org.apache.avro.SchemaValidationException: Unable to read schema: 
{
  "type" : "record",
  "name" : "pouac",
  "fields" : [ {
    "name" : "test2",
    "type" : "int",
    "aliases" : [ "test1" ]
  } ]
}
using schema:
{
  "type" : "record",
  "name" : "pouac",
  "fields" : [ {
    "name" : "test1",
    "type" : "int"
  }, {
    "name" : "test2",
    "type" : "int"
  } ]
}
    at org.apache.avro.ValidateMutualRead.canRead(ValidateMutualRead.java:70)
    at org.apache.avro.ValidateCanBeRead.validate(ValidateCanBeRead.java:39)
    at org.apache.avro.ValidateAll.validate(ValidateAll.java:51)
    at ca.junctionbox.soavro.Main.main(Main.java:47)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.codehaus.mojo.exec.ExecJavaMojo$1.run(ExecJavaMojo.java:294)
    at java.lang.Thread.run(Thread.java:748)

别名通常用于模式演化中的向后兼容性,允许从不同的/遗留密钥到公共密钥名的映射。由于您的编写器模式没有通过使用联合将test1和test2字段视为“可选的”,所以我看不出您希望这种转换是什么场景。如果您想“删除”test1字段,那么可以通过将它排除在v2模式规范之外来实现。任何可以应用读取器方案的读取器都会使用test1模式定义忽略v2。

说明我所说的进化论是什么意思;

v1模式

代码语言:javascript
复制
{
  "type": "record",
  "name": "pouac",
  "fields": [
    {
        "name": "test1",
        "type": "int"
    }]
}

v2模式

代码语言:javascript
复制
{
  "type": "record",
  "name": "pouac",
  "fields": [
    {
        "name": "test2",
        "type": "int",
        "aliases": ["test1"]
    }]
}

您可以在v1格式中获得字节的数据,并引入v2格式,该格式将test1字段重命名为test2。别名将允许您对v1和v2数据执行映射-减少作业、Hive查询等,而无需首先主动重写所有旧的v1数据。注意,这假设字段的类型和语义没有变化。

票数 8
EN

Stack Overflow用户

发布于 2017-12-24 22:52:15

您可以运行java -jar avro-tools-1.8.2.jar tojson来查看帮助,它告诉您可以使用以下命令:

代码语言:javascript
复制
java -jar avro-tools-1.8.2.jar tojson record.avro > tost.json

这将输出到文件:

代码语言:javascript
复制
{"test1":1,"test2":2}

您也可以用--pretty论断来调用它:

代码语言:javascript
复制
java -jar avro-tools-1.8.2.jar tojson --pretty record.avro > tost.json

输出会很好:

代码语言:javascript
复制
{
  "test1" : 1,
  "test2" : 2
}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/47963172

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档