1

This is a follow up to the following question: Fastest way to get the names of the fields of a case class in Scala

I'm trying to find a simple way to provide fast custom serialization (lets say to a list of tuples of (String, Object), which can be converted into a db row in production or an in memory map in unit testing) to a family of case classes in Scala, and it seems that keeping a cached list of a fields of the class may be a promising way of doing this. However, I'm not sure about the cleanest way to do this. I know I can do something like the following:

case class Playlist(
  val id: Option[Long],
  val title: Option[String],
  val album: Option[String],
  val artist: Option[String],
  val songId: Option[UUID]) {
  def serialize = Playlist.fields.map(f => (f.getName, f.get(this)))
}

object Playlist {
  val empty = Playlist(None, None, None, None, None)
  val fields = Playlist.empty.getClass.getDeclaredFields.toList
  fields foreach { _.setAccessible(true) }
}

There a are a couple of things I don't like about this, however:

  1. I don't want to have to use empty from the companion class just to get a cached list of fields
  2. I don't want to have to declare the serialization logic for each case class for which I want this serialization behavior. There are probably a few ways of getting around this, but I'm not sure of the cleanest way that will give correct behavior (worried about mixing reflection and inheritance)

What's the cleanest way to achieve this in Scala?

Community
  • 1
  • 1
jonderry
  • 23,013
  • 32
  • 104
  • 171
  • 2
    So you tried [scala pickling](http://lampwww.epfl.ch/~hmiller/pickling/) and found it not fast enough or stumbled upon some problems? If you haven't tried it yet, why? So far it seems to be fastest, yet clean approach. – om-nom-nom May 27 '14 at 19:24
  • I didn't know about it, but I'd prefer to stay within the core scala libraries if possible for this application. – jonderry May 27 '14 at 19:31
  • It's worth looking at :-) I'm just a bit not comfortable with your approach, because it seems that you inventing over engineered solution to the problem which is already solved elegantly (again, just my **opinion**) – om-nom-nom May 27 '14 at 19:34
  • Thanks, I will look into it, but I'm still interested into the answer to the original question since it would at least give a better understanding of Scala, as well as facilitate a relatively simple and efficient solution that has no additional dependency on a third party library. – jonderry May 27 '14 at 20:25
  • Per my edit: I'm interested specifically in getting into a generic format that will make serializing to a db or in memory test db easy (for example, a list of tuples of field name cross field val). I haven't found such functionality in pickle (yet). The fields approach seems pretty performant, and the code is fairly simple, but I don't know how to make it generic. – jonderry May 27 '14 at 21:33

1 Answers1

2

I think it would be simplest to keep a cache map of Class[_] -> fields separately from any individual case class, such as in a global singleton with a method serialize(instance). This way you don't have to write any extra code in the classes you wish to serialize.

Another way could be to create a trait to mixin to the case classes' companion objects, with the cached list of fields, and an implicit wrapper class to add the serialize method. You can use an implicit ClassTag to initialize fields:

abstract class MyCompanion[T](implicit ctag: ClassTag[T]) {
    private val fields = ctag.runtimeClass.getDeclaredFields.toList
    fields foreach { _.setAccessible(true) }
    implicit class AddSerializeMethod(obj: T) {
        def serialize = fields.map(f => (f.getName, f.get(obj)))
    }
}

case class C(...) { ... }
object C extends MyCompanion[C]

Unfortunately, it seems you can't make AddSerializeMethod a value class this way.

Dan Getz
  • 8,774
  • 6
  • 30
  • 64
  • Second solution is nice, though I don't quite understand how the implicit class works yet. Or value class. – jonderry May 28 '14 at 06:47
  • [`implicit class`](http://docs.scala-lang.org/sips/completed/implicit-classes.html) is the same as adding an implicit conversion that creates an instance of the wrapper class. So whenever you call `serialize`, the wrapper `AddSerializeMethod` instance is created, then `serialize` is called on that. – Dan Getz May 28 '14 at 10:20