问题
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