avro中不处理循环引用

6qftjkof  于 2021-06-02  发布在  Hadoop
关注(0)|答案(1)|浏览(492)

avro附带了一个名为avro tools的工具,可以用来在json、avro模式(.avsc)和二进制格式之间进行转换。但它不适用于循环引用。
我们有两个文件:
circular.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
      }
   ]
}

循环.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工具
java-jar avro-tools-1.7.6.jar fromjson--模式文件circular.avsc circular.json
输出
2014-06-09 14:29:17.759 java[55860:1607]无法从scdynamicstore objavro.codenullavro.schema加载领域Map信息?{“type“:”record“,”name“:”parent“,”namespace“:”bigdata.example.avro“,”fields“:[{”name“:”name“,”type“:[”null“,”string“,”default“:”null},{”name“,”child“,”type“,”record“,”name“:”child“,”fields“:[{”name“:”name“,”type“:[”null“,”string“,”default“:”null},{”name“:”parent“,”type“:[”null“,”parent“,”default“:”null}],“default”:null}]}?'???k?jh!??ė?线程“main”org.apache.avro.avrotypeexception中出现异常:应为start union。在org.apache.avro.io.jsondecoder.error(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
      }
   ]
}

循环.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加载领域Map信息?{“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}]}?x?n??o?m?`abexception in thread“main”java.lang.stackoverflowerror
在org.apache.avro.io.parsing.symbol.flattedSize(symbol。java:212)
在org.apache.avro.io.parsing.symbol$sequence.flattedSize(symbol。java:323)
在org.apache.avro.io.parsing.symbol.flattedSize(symbol。java:216)
在org.apache.avro.io.parsing.symbol$sequence.flattedSize(symbol。java:323)
在org.apache.avro.io.parsing.symbol.flattedSize(symbol。java:216)
在org.apache.avro.io.parsing.symbol$sequence.flattedSize(symbol。java:323)
有人知道我怎样才能使循环引用与avro一起工作吗?

bjg7j2ky

bjg7j2ky1#

我最近也遇到了同样的问题,并以一种变通的方式解决了,希望能有所帮助。
基于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);
}

generate的思想是检查源文件中的类型是否在union中找到。
这里还有一些问题:
此解决方案不支持“record”、“enum”或“fixed”avro类型,因为这些类型需要用户定义的名称。e、 g.如果您想要联合“type”:[“null”,{“name”:“”,“type”:“record”,“fields”:…}],此代码将不起作用。对于基元类型,应该可以这样做。但是在你把它用于你的项目之前,请先测试一下。
就我个人而言,我认为记录不应该是空的,因为我认为记录是我需要确保存在的,如果缺少了什么,那就意味着我有更大的问题。如果可以省略的话,我更喜欢使用“map”作为类型,而不是在定义模式时使用“record”。
希望这能有所帮助。

相关问题