Scala: Как преобразовать вложенные / сложные данные в avro, используя avro4s? - PullRequest
0 голосов
/ 02 марта 2019

У меня есть Scala 2.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 класса дел.

Я протестировал схему на основе этих классов, и она отлично выглядит.
Итак, генерация схемы хороша.

Теперь,Я создаю необходимые объекты в соответствии с классом дел.

Когда я пытаюсь записать файл 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, я изменил свой заголовок и класс тела, чтобы использовать Option [String] для всех значений, которые, как ожидается, будут нулевыми.Но все та же проблема.

Изменения в классах дел заголовка и данных, производимых в соответствии со схемой и записью.Правильно ли создана следующая запись?

Пожалуйста, сообщите?

ОБНОВЛЕНИЕ 2:
Я думаю, что проблема с Нуль.Ожидается, что записи будут иметь несколько атрибутов как NULL.Поскольку я изменил на Option [String], его значение должно быть None, а не NULL.Я новичок в Scala, поэтому все еще понимаю его типы данных.

Итак, изменение значения с нуля на None работает.

Однако у меня все еще есть один вопрос.Если мои атрибуты - Option [String], как он переводится в Avro?Если мое значение равно None, переводится ли оно на Avro null?

1 Ответ

0 голосов
/ 22 мая 2019

Ваша проблема в том, что ваша схема не определяет поля как обнуляемые.Если у вас есть нулевые значения, это должно поддерживаться в схеме.Чтобы сделать это в Avro, вы должны создать «объединение» из двух схем: одна представляет собой схему NULL, а другая - «реальный тип».Например, посмотрите на эту схему.

{
  "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.

Теперь вам не нужно создавать эту схему вручную (как вы делали в своем посте),Вы можете использовать макрос AvroSchema, чтобы творить магию для вас на основе класса дела.

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

Либо создайте схему из класса дел, где поле Nullable равноопределяется как Option, или обновите вашу схему, вручную свернутую, чтобы использовать объединение {null, T}.

...