Scala case classes with new firebase-server-sdk

泪湿孤枕 提交于 2020-02-24 20:59:11

问题


Does the new server SDK for firebase firebase-server-sdk (3.0.1) support Scala case class deserialization? The previous firebase java sdk used jackson which you could bolt in a scala module to support case classes. It's unclear if its possible to do something similar with the new SDK? Does it use Gson or some custom class mapper?

In the simplest example:

case class Person(firstName: String, lastName: String, age: Int)

With a Firebase listener setup such as:

var options = new FirebaseOptions.Builder()
  .setDatabaseUrl("https://<your firebase>.firebaseio.com")
  .setServiceAccount(new FileInputStream("firebase-auth.json"))
  .build()

FirebaseApp.initializeApp(options);

var ref = FirebaseDatabase.getInstance().getReference("somepath")
ref.addListenerForSingleValueEvent(new ValueEventListener {
  override def onDataChange(dataSnapshot: DataSnapshot): Unit = {
    println(dataSnapshot.getValue(classOf[Person]))
  }

  override def onCancelled(databaseError: DatabaseError): Unit = {
    println(databaseError.getMessage)
  }
})

This will fail on the getValue call dataSnapshot.getValue(classOf[Person]) with the exception:

Exception in thread "FirebaseDatabaseEventTarget" com.google.firebase.database.DatabaseException: No properties to serialize found on class Person
    at com.google.firebase.database.utilities.encoding.CustomClassMapper$BeanMapper.<init>(CustomClassMapper.java:495)
    at com.google.firebase.database.utilities.encoding.CustomClassMapper.loadOrCreateBeanMapperForClass(CustomClassMapper.java:285)
    at com.google.firebase.database.utilities.encoding.CustomClassMapper.convertBean(CustomClassMapper.java:379)
    at com.google.firebase.database.utilities.encoding.CustomClassMapper.deserializeToClass(CustomClassMapper.java:187)
    at com.google.firebase.database.utilities.encoding.CustomClassMapper.convertToCustomClass(CustomClassMapper.java:61)
    at com.google.firebase.database.DataSnapshot.getValue(DataSnapshot.java:181)
    at PetEventsNodeActorSpec$$anonfun$2$$anonfun$apply$mcV$sp$2$$anonfun$apply$mcV$sp$3$$anon$1.onDataChange(PetEventsNodeActorSpec.scala:290)
    at com.google.firebase.database.Query$1.onDataChange(Query.java:147)
    at com.google.firebase.database.core.ValueEventRegistration.fireEvent(ValueEventRegistration.java:57)
    at com.google.firebase.database.core.view.DataEvent.fire(DataEvent.java:45)
    at com.google.firebase.database.core.view.EventRaiser$1.run(EventRaiser.java:35)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)

I've tried adding annotations to the class such as @BeanProperty but then get:

Exception in thread "FirebaseDatabaseEventTarget" com.google.firebase.database.DatabaseException: Class Person is missing a constructor with no arguments

Rather than go down the rabbit hole of annotating or adding code to every case class, any ideas on getting scala case classes to play nicely with the new firebase sdk?


回答1:


AFAIK, there isn't a way to use a case class directly. I ended up creating plain classes with @BeanProperty annotations and then converting them to case classes. The reason for

Exception in thread "FirebaseDatabaseEventTarget" com.google.firebase.database.DatabaseException: Class Person is missing a constructor with no arguments

is because your class constructor must be nullary (i.e. it cannot take any arguments):

import scala.beans.BeanProperty

case class Person(firstName: String, lastName: String, age: Int) {
  def toBean: PersonBean = {
    val person = new PersonBean()
    person.firstName = firstName
    person.lastName = lastName
    person.age = age
    person
  }
}

class PersonBean() {
  @BeanProperty var firstName: String = ""
  @BeanProperty var lastName: String = ""
  @BeanProperty var age: Int = 0
  def toCase: Person = Person(firstName, lastName, age)
}

var ref = FirebaseDatabase.getInstance().getReference("somepath")
ref.addListenerForSingleValueEvent(new ValueEventListener {
  override def onDataChange(dataSnapshot: DataSnapshot): Unit = {
    val record = dataSnapshot.getValue(classOf[PersonBean])
    val person = if (record != null) record.toCase else null
  }

  override def onCancelled(databaseError: DatabaseError): Unit = {
    println(databaseError.getMessage)
  }
})


来源:https://stackoverflow.com/questions/38533778/scala-case-classes-with-new-firebase-server-sdk

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!