Avro模式中的多态性和继承

时间:2014-01-01 00:14:35

标签: avro

是否可以编写一个Avro架构/ IDL来生成扩展基类或实现接口的Java类? 似乎生成的Java类扩展了org.apache.avro.specific.SpecificRecordBase。因此,工具可能是要走的路。但是,我不知道这是否可能。

我看过一些示例,建议在每个特定模式中定义一个显式的“类型”字段,其中包含的关联多于继承语义。

我在我的工厂类和代码的其他部分使用我的基类,使用像<T extends BaseObject>这样的泛型。目前,我有从JSON Schema生成的代码,它支持继承。

另一个侧面问题:您是否可以使用IDL来定义没有协议定义的记录?我认为答案是否定的,因为编译器抱怨缺少协议关键字。

帮助表示感谢!感谢。

5 个答案:

答案 0 :(得分:15)

我找到了解决此问题的更好方法。看一下Avro中的Schema生成源,我发现在类内部,类生成逻辑使用Velocity模式来生成类。

我修改了record.vm模板以实现我的特定界面。有一种方法可以使用maven构建插件中的templateDirectory配置来指定velocity目录的位置。

我也转而使用SpecificDatumWriter代替reflectDatumWriter

<plugin>
  <groupId>org.apache.avro</groupId>
  <artifactId>avro-maven-plugin</artifactId>
   <version>${avro.version}</version>
   <executions>
    <execution>
      <phase>generate-sources</phase>
      <goals>
        <goal>schema</goal>
      </goals>
      <configuration>
         <sourceDirectory>${basedir}/src/main/resources/avro/schema</sourceDirectory>
         <outputDirectory>${basedir}/target/java-gen</outputDirectory>
         <fieldVisibility>private</fieldVisibility>
         <stringType>String</stringType>
         <templateDirectory>${basedir}/src/main/resources/avro/velocity-templates/</templateDirectory>
       </configuration>
    </execution>
  </executions>
</plugin>

答案 1 :(得分:1)

我决定使用ReflectData API在运行时从类生成Schema,然后使用ReflectDatumWriter进行序列化。 使用反射会更慢。但是,看起来架构是在内部缓存的。如果我发现性能问题,我会报告。

Schema schema = ReflectData.AllowNull.get().getSchema(sourceObject.getClass());
ReflectDatumWriter<T> reflectDatumWriter = new ReflectDatumWriter<>(schema);

DataFileWriter<T> writer = new DataFileWriter<>(reflectDatumWriter);
try {
    writer.setCodec(CodecFactory.snappyCodec());
    writer.create(schema, new File("data.avro"));
    writer.append(sourceObject);
    writer.close();
}
catch (IOException e) {
    // log exception
}

答案 2 :(得分:1)

如果我在这里写出我正是为这种情况创建了maven插件-https://github.com/tunguski/interfacer,希望对其他人有帮助。

它会检查自动生成的类,并检查它们是否符合特定包中classpath上的接口。如果是,则将接口添加到该类。它与通用接口兼容,至少在我必须处理的基本示例中。

该插件不是avro专用的,可以作为生成的代码后处理器使用,因此也可以在其他情况下使用。

map(lambda val:  10 if val < 2 else 50, [1,2,3]))
<!-- 
  post process avro generated sources and add interfaces from package
  pl.matsuo.interfacer.showcase to every generated class that has 
  all methods from specific interface
 -->
<plugin>
    <groupId>pl.matsuo.interfacer</groupId>
    <artifactId>interfacer-maven-plugin</artifactId>
    <version>0.0.6</version>
    <executions>
        <execution>
            <configuration>
                <interfacesDirectory>${project.basedir}/src/main/java</interfacesDirectory>
                <interfacePackage>pl.matsuo.interfacer.showcase</interfacePackage>
            </configuration>
            <goals>
                <goal>add-interfaces</goal>
            </goals>
        </execution>
    </executions>
</plugin>

答案 3 :(得分:0)

我发现这个问题也有类似的问题。就我而言,我只需要强加 marker interface ,并且只强加 some 类型(以便以后区分特定的类)。感谢您的回答,我更深入地研究了record.vm模板的结构。我发现可以在"javaAnnotation": "my.full.AnnotationName"定义JSON中定义.avsc键。然后将@my.full.AnnotationName添加到生成的类中。

诚然,此解决方案最终不是基于标记器接口构建的,尽管对我来说足够好,并且保持模板不变是很大的优势。

答案 4 :(得分:0)

我遵循https://www.infoq.com/articles/ApacheAvro/来实现继承。这也使多态性成为现实(我需要)。

一分。 在声明时 {"name": "user", "type": com.navteq.avro.FacebookUser }, 确保您使用双引号,例如{"name": "user", "type": "com.navteq.avro.FacebookUser" },

如果我不这样做,那我将得到如下错误

> org.apache.avro.SchemaParseException: org.codehaus.jackson.JsonParseException: Unexpected character ('c' (code 99)): expected a valid value (number, String, array, object, 'true', 'false' or 'null')