我有一个成功输出Avro文件的管道,如下所示:
@DefaultCoder(AvroCoder.class)
class MyOutput_T_S {
T foo;
S bar;
Boolean baz;
public MyOutput_T_S() {}
}
@DefaultCoder(AvroCoder.class)
class T {
String id;
public T() {}
}
@DefaultCoder(AvroCoder.class)
class S {
String id;
public S() {}
}
...
PCollection<MyOutput_T_S> output = input.apply(myTransform);
output.apply(AvroIO.Write.to("/out").withSchema(MyOutput_T_S.class));
如何使用参数化输出MyOutput<T, S>
(其中T
和S
都是使用反射的Avro代码可以重现此确切行为。)
主要问题是Avro反射不适用于参数化类型。所以基于这些回答:
1)我想我需要写一个自定义的CoderFactory
但是,我很难弄清楚它是如何工作的(我很难找到例子)。奇怪的是,一个完全天真的编码器工厂似乎让我运行管道并使用DataflowAssert检查正确的输出:
cr.RegisterCoder(MyOutput.class, new CoderFactory() {
@Override
public Coder<?> create(List<? excents Coder<?>> componentCoders) {
Schema schema = new Schema.Parser().parse("{\"type\":\"record\,"
+ "\"name\":\"MyOutput\","
+ "\"namespace\":\"mypackage"\","
+ "\"fields\":[]}"
return AvroCoder.of(MyOutput.class, schema);
}
@Override
public List<Object> getInstanceComponents(Object value) {
MyOutput<Object, Object> myOutput = (MyOutput<Object, Object>) value;
List components = new ArrayList();
return components;
}
虽然我现在可以成功断言输出,但我希望这不会因为写入文件而削减它。我还没弄明白我应该如何使用提供的componentCoders
来生成正确的架构,如果我尝试将T
或S
的架构推送到{{1我得到了:
fields
2)假设我弄清楚如何编码java.lang.IllegalArgumentException: Unable to get field id from class null
。我将什么传递给MyOutput
?如果我传递了AvroIO.Write.withSchema
或模式,我会收到类型不匹配错误。
答案 0 :(得分:1)
我认为有两个问题(如果我错了,请纠正我):
MyOutput<T, S>
的各种参数化提供编码器?MyOutput<T, S>
AvroIO.Write
对文件进行值{。}}。{/ li>
醇>
第一个问题是通过在您找到的链接问题中注册CoderFactory
来解决。
您的天真编码器可能允许您毫无问题地运行管道,因为序列化正在被优化掉。当然,没有字段的Avro架构将导致这些字段在序列化+反序列化往返中被删除。
但假设您使用字段填写架构,您对CoderFactory#create
的处理方式看起来正确。我不知道邮件的确切原因java.lang.IllegalArgumentException: Unable to get field id from class null
,但是AvroCoder.of(MyOutput.class, schema)
的调用应该适用于适当汇编的schema
。如果这有问题,更多细节(例如堆栈轨道的其余部分)将会有所帮助。
但是,覆盖CoderFactory#getInstanceComponents
应该返回一个值列表,每个类型参数MyOutput
一个。像这样:
@Override
public List<Object> getInstanceComponents(Object value) {
MyOutput<Object, Object> myOutput = (MyOutput<Object, Object>) value;
return ImmutableList.of(myOutput.foo, myOutput.bar);
}
第二个问题可以使用与第一个相同的支持代码来回答,但在其他方面是独立的。 AvroIO.Write.withSchema
始终显式使用提供的架构。它确实使用了AvroCoder
,但这实际上是一个实现细节。只需提供兼容的架构即可 - 必须为要输出T
的{{1}}和S
的每个值组合架构。