Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NotSerializableException for `Map[String, String]` alias

I'm trying to send an object to a remote actor and I got this exception:

ERROR akka.remote.EndpointWriter - Transient association error (association remains live)
java.io.NotSerializableException: scala.collection.immutable.MapLike$$anon$2

The object being serialized is a case class:

case class LocationReport(idn: String, report: String, timestamp: Option[String], location: Attr, status: Attr, alarms: Attr, network: Attr, sensors: Attr) extends Message(idn) {

  val ts = timestamp getOrElse location("fix_timestamp")

  def json =
    (report ->
      ("TIME" -> ts) ~
      ("location" -> location) ~
      ("alarms" -> alarms) ~
      ("network" -> network) ~
      ("sensors" -> ((status ++ sensors) + ("CUSTOMCLOCK" -> Report.decodeTimestamp(ts)))))
}

And Attr is a type re-definition:

type Attr = Map[String, String]

The Message class is pretty simple:

abstract class Message(idn: String) {
  def topic = idn
  def json(): JValue
}

I'm wondering if the type alias/redefinition is confusing the serializer. I think I'm using ProtoBuf serialization, but I do see JavaSerializer in the stacktrace.

More Debugging Info

I newed up a JavaSerializer and individually serialized each of the Maps. Only one (alarms) fails to serialize. Here's the toString of each of them:

This one failed:

alarms = Map(LOWBATTERY -> 1373623446000)

These succeeded:

location = Map(a_value -> 6, latitude -> 37.63473, p_value -> 4, longitude -> -97.41459, fix_timestamp -> 3F0AE7FF, status -> OK, fix_type -> MSBL, CUSTOMCLOCK -> 1373644159000)
network = Map(SID -> 1271, RSSI -> 85)
sensors = Map(HUMIDITY -> -999, PRESSURE -> -999, LIGHT -> -999  9:52 AM)
status = Map(TEMPERATURE_F -> 923, CYCLE -> 4, TEMPERATURE1_C -> 335, CAP_REMAINING -> 560, VOLTAGE -> 3691, CAP_FULL -> 3897)
like image 463
kelloti Avatar asked Jul 17 '13 20:07

kelloti


2 Answers

The problem is that Map.mapValues produces an object that's not serializable. When alarms was created, it's run through something like alarms.mapValues(hex2Int). The problem and workaround is described here:

https://issues.scala-lang.org/browse/SI-7005

In short, the solution is to do alarms.mapValues(hex2Int).map(identity)

like image 93
kelloti Avatar answered Oct 31 '22 17:10

kelloti


Not sure whether this works in all cases but my workaround was simply to convert the map into a sequence (just .toSeqbefore the sequence) before serialization. toMap should give you the same map after deserialization.

like image 1
lex82 Avatar answered Oct 31 '22 15:10

lex82