我有Scala2.12并导入了库avro4s,根据我的需求遵循了链接。
基本上,我的avro模式如下:仅示例:
Schema: {"name": "person","type": "record","fields": [{"name": "address","type": {"type" : "record","name" : "AddressUSRecord","fields" : [{"name": "streetaddress", "type": "string"},{"name": "city", "type":"string"}]}}]}所以我创建了3个案例类。
我基于这些类测试了模式,它看起来很好。
因此,模式生成是很好的。
现在,我按照case类创建所需的对象。
当我试图写avro文件时,我得到了空指针异常。
错误:
Exception in thread "main" java.lang.NullPointerException
at org.apache.avro.util.Utf8$2.toUtf8(Utf8.java:123)
at org.apache.avro.util.Utf8.getBytesFor(Utf8.java:172)
at org.apache.avro.util.Utf8.<init>(Utf8.java:39)
at com.sksamuel.avro4s.Encoder$StringEncoder$.encode(Encoder.scala:73)
at com.sksamuel.avro4s.Encoder$StringEncoder$.encode(Encoder.scala:68)
at com.sksamuel.avro4s.Encoder$.encodeField(Encoder.scala:401)
at com.sksamuel.avro4s.Encoder$.encodeFieldLazy(Encoder.scala:379)
at MyClass$$anon$4$$anon$5.encode(MyClass.scala:90)
at MyClass$$anon$4$$anon$5.encode(MyClass.scala:90)
at com.sksamuel.avro4s.Encoder$.encodeField(Encoder.scala:401)
at com.sksamuel.avro4s.Encoder$.encodeFieldNotLazy(Encoder.scala:373)
at MyClass$$anon$4.encode(MyClass.scala:90)
at MyClass$$anon$4.encode(MyClass.scala:90)
at com.sksamuel.avro4s.AvroDataOutputStream.$anonfun$x$1$2(AvroDataOutputStream.scala:35)
at com.sksamuel.avro4s.AvroDataOutputStream.$anonfun$x$1$2$adapted(AvroDataOutputStream.scala:34)
at com.sksamuel.avro4s.AvroDataOutputStream.write(AvroDataOutputStream.scala:46)
at MyClass$.delayedEndpoint$MyClass$1(MyClass.scala:91)
at MyClass$delayedInit$body.apply(MyClass.scala:42)
at scala.Function0.apply$mcV$sp(Function0.scala:34)
at scala.Function0.apply$mcV$sp$(Function0.scala:34)
at scala.runtime.AbstractFunction0.apply$mcV$sp(AbstractFunction0.scala:12)
at scala.App.$anonfun$main$1$adapted(App.scala:76)
at scala.collection.immutable.List.foreach(List.scala:388)
at scala.App.main(App.scala:76)
at scala.App.main$(App.scala:74)
at MyClass$.main(MyClass.scala:42)
at MyClass.main(MyClass.scala)代码:
//import java.io.File
import com.sksamuel.avro4s.{AvroOutputStream, AvroSchema}
import java.io.File
//case class Person(name: String, age: Int)
//case class Book(title: String, year: Int, owner: Person, authors: Seq[Person])
// case class as per schema
object MyClass extends App {
val outFile = "/path/TestScala.avro"
// val schema = AvroSchema[Book]
println("Hello, World!")
// println(schema)
val head = header(
prop1="val1"
prop2=null
)
val pnlBody = pnlData(
<corresponsing property vlaues, some with null>
)
val record = MyClass(header = head, body = pnlBody)
val schema = AvroSchema[MyClass]
println(schema)
println(record)
val os = AvroOutputStream.data[MyClass].to(new File(outFile)).build(schema)
os.write(record)
os.flush()
os.close()
}基本上,基于我的模式,我想了解如何成为我的最终记录对象?
更新
根据@Antot和@Daniel的以下建议,我将头类和body类更改为对所有预期为null的值使用OptionString。但问题仍然是一样的。
对标头和数据的case类的更改,按照架构和记录进行。是否正确创建了下面的记录?
请指点?
更新2:
我认为诺尔斯家族的问题。预期这些记录几乎没有空的属性。由于我更改为OptionString,它的值应该是None和null。我是Scala的新手,所以我仍然了解它的数据类型。
因此,将值从null更改为None现在有效。
不过,我还有一个问题要问。如果我的属性是OptionString,那么它是如何转换为Avro的?如果我的值为None,它会转换为Avro null吗?
发布于 2019-05-22 04:03:41
您的问题是,您的架构没有将字段定义为可空。如果您有空值,则必须在架构中支持该值。要在Avro中这样做,您必须创建两个模式的“联合”--一个是空模式,另一个是“真正类型”。看看这个模式。
{
"type": "record",
"name": "MyClass",
"namespace": "com.sksamuel.avro4s",
"fields": [
{
"name": "a",
"type": [
"null",
"string"
],
"default": null
}
]
}这是一个记录类型com.sksamuel.avro4s.MyClass,它有一个字段a。然后a的类型可以是null或string。因此,在写出这种类型的记录时,可以对字段a使用null或字符串。
现在,您不必手工创建此模式(正如您在文章中所做的那样)。您可以使用AvroSchema宏根据case类为您执行魔术。
val schema = AvroSchema[MyClass]。
使用此宏时,如果将类型定义为Option[T],则将创建一个可空的联合。所以你可以这么做,
case class MyClass(a: Option[String])您将得到上面相同的模式。如果你这么做了
case class MyClass(a: String)然后,模式将是:
{
"type": "record",
"name": "MyClass",
"namespace": "com.sksamuel.avro4s",
"fields": [
{
"name": "a",
"type": "string"
}
]
}tl;dr
要么从可空字段定义为选项的case类创建架构,要么更新手动模式以使用{null,T}的联合。
https://stackoverflow.com/questions/54952787
复制相似问题