当前位置: 首页 > 知识库问答 >
问题:

Avro中未处理的循环引用

璩华辉
2023-03-14

有一个名为Avro-Tools的工具,它随Avro一起提供,可以用来在JSON、Avro-Schema(.avsc)和二进制格式之间进行转换。但它不能用于循环引用。

我们有两个文件:

>

  • 循环.avsc(由Avro生成)

    json(由jackson生成,因为它有循环引用,Avro不喜欢这样)。

    通告.AVSC

    {
       "type":"record",
       "name":"Parent",
       "namespace":"bigdata.example.avro",
       "fields":[
          {
             "name":"name",
             "type":[
                "null",
                "string"
             ],
             "default":null
          },
          {
             "name":"child",
             "type":[
                "null",
                {
                   "type":"record",
                   "name":"Child",
                   "fields":[
                      {
                         "name":"name",
                         "type":[
                            "null",
                            "string"
                         ],
                         "default":null
                      },
                      {
                         "name":"parent",
                         "type":[
                            "null",
                            "Parent"
                         ],
                         "default":null
                      }
                   ]
                }
             ],
             "default":null
          }
       ]
    }
    

    circular.json

    {
       "@class":"bigdata.example.avro.Parent",
       "@circle_ref_id":1,
       "name":"parent",
       "child":{
          "@class":"bigdata.example.avro.DerivedChild",
          "@circle_ref_id":2,
          "name":"hello",
          "parent":1
       }
    }
    

    在上面运行avro-tools的命令

    Java-jar avro-tools-1.7.6.jar fromjson--schema-file circular.avsc circular.json

    输出

    2014-06-09 14:29:17.759 Java[55860:1607]无法从SCDynamicStore objavro.codeNullavro.schema加载领域映射信息?{“type”:“record”,“name”:“parent”,“namespace”:“bigdata.example.avro”,“fields”:[{“name”:“name”,“type”:[“null”,“string”],“default”:null},{“name”:“child”,“type”:[“null”,“child”,“fields”:[{“name”:“name”,“default”:null},“default”:null},{“nype”:“record”,“name”:“在org.apache.avro.io.jsonDecoder.error处获得VALUE_STRING(jsonDecoder.java:697)

    在org.apache.avro.io.jsonDecoder.readIndex(jsonDecoder.java:441)

    在org.apache.avro.io.resolvingdecoder.doAction(resolvingdecoder.java:229)

    其他一些JSON值尝试使用相同的模式,但没有成功

    JSON 1

    {
       "name":"parent",
       "child":{
          "name":"hello",
          "parent":null
       }
    }
    

    JSON 2

    {
       "name":"parent",
       "child":{
          "name":"hello",
       }
    }
    

    JSON 3

     {
       "@class":"bigdata.example.avro.Parent",
       "@circle_ref_id":1,
       "name":"parent",
       "child":{
          "@class":"bigdata.example.avro.DerivedChild",
          "@circle_ref_id":2,
          "name":"hello",
          "parent":null
       }
    }
    

    删除一些“可选”元素

    通告.AVSC

    {
       "type":"record",
       "name":"Parent",
       "namespace":"bigdata.example.avro",
       "fields":[
          {
             "name":"name",
             "type":
                "string",
             "default":null
          },
          {
             "name":"child",
             "type":
                {
                   "type":"record",
                   "name":"Child",
                   "fields":[
                      {
                     "name":"name",
                     "type":
                        "string",
                     "default":null
                      },
                      {
                         "name":"parent",
                         "type":
                            "Parent",
                         "default":null
                      }
                   ]
                },
             "default":null
          }
       ]
    }
    

    circular.json

     {
       "@class":"bigdata.example.avro.Parent",
       "@circle_ref_id":1,
       "name":"parent",
       "child":{
          "@class":"bigdata.example.avro.DerivedChild",
          "@circle_ref_id":2,
          "name":"hello",
          "parent":1
       }
    }
    

    输出

    2014-06-09 15:30:53.716 Java[56261:1607]无法从SCDynamicStore Objavro.codeNullavro.Schema加载领域映射信息?{“type”:“record”,“name”:“parent”,“namespace”:“bigdata.example.avro”,“fields”:[{“name”:“name”,“type”:“string”,“default”:null},{“name”:“child”,“type”:{“type”:“child”,“fields”:[{“name”:“name”

    在org.apache.avro.io.parsing.symbol.FlatenedSize(符号.java:212)

    在org.apache.avro.io.parsing.symbol$sequence.flattenedsize(Symbol.java:323)

    在org.apache.avro.io.parsing.symbol.FlatenedSize(符号.java:216)

    在org.apache.avro.io.parsing.symbol$sequence.flattenedsize(Symbol.java:323)

    在org.apache.avro.io.parsing.symbol.FlatenedSize(符号.java:216)

    在org.apache.avro.io.parsing.symbol$sequence.flattenedsize(Symbol.java:323)

    有没有人知道我如何让循环参考与Avro一起工作?

  • 共有1个答案

    仲孙才捷
    2023-03-14

    我最近遇到了同样的问题,并以一种变通的方式解决了这个问题,希望它能起到帮助作用。

    基于Avro规范:

    JSON编码除了联合之外,JSON编码与用于编码字段默认值的编码相同。

    union的值在JSON中编码如下:

    • 如果其类型为null,则将其编码为JSON null;
    • 否则,它被编码为一个具有一个名称/值对的JSON对象,该名称/值对的名称是类型的名称,其值是递归编码的值。对于Avro的命名类型(record、fixed或enum),使用用户指定的名称,对于其他类型,使用类型名称。

    例如,联合模式[“null”,“string”,“Foo”](其中Foo是记录名)将编码:

    • 空为空;
    • 字符串“a”作为{“string”:“a”};
    • 和Foo实例为{“Foo”:{...}},其中{...}指示Foo实例的JSON编码。

    如果源文件不能被改变以符合要求,也许我们不得不改变代码。所以我从avro-1.7.7包中定制了原始的org.apache.avro.io.JsonDecoder类,并创建了自己的类MyJsonDecoder。

    下面是除了创建新的构造函数和类名之外,我更改的键:

        @Override
    public int readIndex() throws IOException {
        advance(Symbol.UNION);
        Symbol.Alternative a = (Symbol.Alternative) parser.popSymbol();
    
        String label;
        if (in.getCurrentToken() == JsonToken.VALUE_NULL) {
            label = "null";
    //***********************************************
    // Original code: according to Avor document "JSON Encoding":
    // it is encoded as a Json object with one name/value pair whose name is
    //   the type's name and whose value is the recursively encoded value.
    // Can't change source data, so remove this rule.
    //        } else if (in.getCurrentToken() == JsonToken.START_OBJECT &&
    //                in.nextToken() == JsonToken.FIELD_NAME) {
    //            label = in.getText();
    //            in.nextToken();
    //            parser.pushSymbol(Symbol.UNION_END);
    //***********************************************
            // Customized code:
            // Add to check if type is in the union then parse it.
            // Check if type match types in union or not.
        } else {
            label = findTypeInUnion(in.getCurrentToken(), a);
    
            // Field missing but not allow to be null
            //   or field type is not in union.
            if (label == null) {
                throw error("start-union, type may not be in UNION,");
            }
        }
    //***********************************************
    // Original code: directly error out if union
    //        } else {
    //                throw error("start-union");
    //        }
    //***********************************************
        int n = a.findLabel(label);
        if (n < 0)
            throw new AvroTypeException("Unknown union branch " + label);
        parser.pushSymbol(a.getSymbol(n));
        return n;
    }
    
    /**
     * Method to check if current JSON token type is declared in union.
     * Do NOT support "record", "enum", "fix":
     * Because there types require user defined name in Avro schema,
     * if user defined names could not be found in Json file, can't decode.
     *
     * @param jsonToken         JsonToken
     * @param symbolAlternative Symbol.Alternative
     * @return String Parsing label, decode in which way.
     */
    private String findTypeInUnion(final JsonToken jsonToken,
                                   final Symbol.Alternative symbolAlternative) {
        // Create a map for looking up: JsonToken and Avro type
        final HashMap<JsonToken, String> json2Avro = new HashMap<>();
    
        for (int i = 0; i < symbolAlternative.size(); i++) {
            // Get the type declared in union: symbolAlternative.getLabel(i).
            // Map the JsonToken with Avro type.
            switch (symbolAlternative.getLabel(i)) {
                case "null":
                    json2Avro.put(JsonToken.VALUE_NULL, "null");
                    break;
                case "boolean":
                    json2Avro.put(JsonToken.VALUE_TRUE, "boolean");
                    json2Avro.put(JsonToken.VALUE_FALSE, "boolean");
                    break;
                case "int":
                    json2Avro.put(JsonToken.VALUE_NUMBER_INT, "int");
                    break;
                case "long":
                    json2Avro.put(JsonToken.VALUE_NUMBER_INT, "long");
                    break;
                case "float":
                    json2Avro.put(JsonToken.VALUE_NUMBER_FLOAT, "float");
                    break;
                case "double":
                    json2Avro.put(JsonToken.VALUE_NUMBER_FLOAT, "double");
                    break;
                case "bytes":
                    json2Avro.put(JsonToken.VALUE_STRING, "bytes");
                    break;
                case "string":
                    json2Avro.put(JsonToken.VALUE_STRING, "string");
                    break;
                case "array":
                    json2Avro.put(JsonToken.START_ARRAY, "array");
                    break;
                case "map":
                    json2Avro.put(JsonToken.START_OBJECT, "map");
                    break;
                default: break;
            }
        }
    
        // Looking up the map to find out related Avro type to JsonToken
        return json2Avro.get(jsonToken);
    }
    

    生成的思想是检查源文件中的类型是否可以在union中找到。

    这里还有一些问题:

    >

  • 解决方案不支持“record”、“enum”或“fixed”Avro类型,因为这些类型需要用户定义名称。例如。如果要联合“type”:[“null”,{“name”:“abc”,“type”:“record”,“fields”:...}],则此代码将不起作用。对于基元类型,这应该起作用。但请在您将其用于您的项目之前对其进行测试

    就我个人而言,我认为记录不应该是空的,因为我认为记录是我需要确保存在的,如果缺少了什么,那意味着我有更大的问题。如果可以省略,我更喜欢在定义模式时使用“map”作为类型,而不是使用“record”。

    希望这能有所帮助。

  •  类似资料:
    • 下面是我的父组件,它包含一个循环的多个输入。如何选择一个来聚焦?在这种情况下,我必须创建动态吗?

    • 问题内容: 我正在尝试在某些类图中围绕类生成运行时包装,但是当图中有一个循环时,我不知道如何处理这种情况。想象有一个类A的字段类型为B,但是类型B的字段类型为A。我想生成类A’和B’,以便类A’的字段类型为B’,而B’的字段类型为B。 A’类型的字段。在字节伙伴中,方法“ defineField”可以接收类型定义的参数。我认为必须有一种方法可以为尚未定义的类型定义TypeDefinition,但我

    • 这里是一个以圆圈为单位的交叉网格,当前为5x5。我试图得到一行5,下面是一行4,然后是3,然后是2等等。我试着改变for循环和值,但什么都不起作用。我需要使用行和列吗? 谢谢!

    • 问题内容: 您使用哪种SQL技巧将数据输入到两个表之间,并在两个表之间使用循环引用。 员工属于部门,部门必须有经理(部门负责人)。 我是否必须禁用约束才能进行插入? 问题答案: 问: 是否必须禁用约束才能进行插入? 答: 在Oracle中,否,如果外键约束为否,则为否 (请参见下面的示例) 对于Oracle: 让我们解压一下: (必须禁用自动提交) 推迟执行外键约束 在“部门”表中插入一行,并为F

    • 问题内容: 我有一个数组(称为),其中包含数百个天文学图像文件的名称。然后处理这些图像。我的代码有效,并且需要几秒钟来处理每个图像。但是,一次只能执行一张图像,因为我是通过循环运行阵列: 没有理由我必须先修改映像,因此可以利用计算机上的所有4个内核,每个内核都通过for循环在不同的映像上运行吗? 我已经阅读了有关该模块的信息,但是不确定如何在我的情况下实现它。我热衷于工作,因为最终我必须在10,0

    • 问题内容: 给定一个以复杂的,循环的方式相互引用的类实例的集合:垃圾收集器是否可能无法释放这些对象? 我隐约记得过去这是JVM中的问题,但我 认为 这在几年前已解决。但是,在jhat中进行的一些调查显示,循环引用是我现在面临的内存泄漏的原因。 注意:我一直给人以JVM能够解析循环引用并从内存中释放这种“垃圾岛”的印象。 但是,我提出这个问题只是为了看看是否有人发现了任何异常。 问题答案: 循环引用