sirthias / borer

Efficient CBOR and JSON (de)serialization in Scala

Home Page:https://sirthias.github.io/borer/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Cannot deserialize empty arrays of non-primitive types

sptz45 opened this issue · comments

The bug

Currently the following code fails with a ClassCastException:

val bytes = Cbor.encode(Array.empty[String]).toByteArray
Cbor.decode(bytes).to[Array[String]].value // throws exceptions

The exception message is:

java.lang.ClassCastException: class [Ljava.lang.Object; cannot be cast to class [Ljava.lang.String; ([Ljava.lang.Object; and [Ljava.lang.String; are in module java.base of loader 'bootstrap')

The code works as expected if we use a primitive type like Int:

val bytes = Cbor.encode(Array.empty[Int]).toByteArray
Cbor.decode(bytes).to[Array[Int]].value // returns the empty array

The root cause

When decoding an array the io.bullet.borer.internal.Util.emptyArray method is used to construct empty arrays. The method is defined as:

  def emptyArray[T](implicit ct: ClassTag[T]): Array[T] =
    (ct.runtimeClass match {
      case java.lang.Byte.TYPE      => Array.emptyByteArray
      case java.lang.Short.TYPE     => Array.emptyShortArray
      case java.lang.Character.TYPE => Array.emptyCharArray
      case java.lang.Integer.TYPE   => Array.emptyIntArray
      case java.lang.Long.TYPE      => Array.emptyLongArray
      case java.lang.Float.TYPE     => Array.emptyFloatArray
      case java.lang.Double.TYPE    => Array.emptyDoubleArray
      case java.lang.Boolean.TYPE   => Array.emptyBooleanArray
      case _                        => Array.emptyObjectArray
    }).asInstanceOf[Array[T]]

The exception is thrown because in Java we cannot cast S[] to T[] if T is a subtype of S (if I recall correctly this has to do with the fact the arrays in Java are covariant).

For example:

jshell> Object[] o = new Object[]{};
o ==> Object[0] {  }

jshell> String[] s = (String[])o;
|  Exception java.lang.ClassCastException: class [Ljava.lang.Object; cannot be cast to class [Ljava.lang.String; ([Ljava.lang.Object; and [Ljava.lang.String; are in module java.base of loader 'bootstrap')
|        at (#2:1)

Thank you, Spiros, for this awesome bug report!
Not simply with a very clear problem description, but also a complete root-cause analysis!
Kudos and huge hats-off to you!

I'll take the fix for this as a nice trigger for the next release, which I'll publish this afternoon.

Thank you @sirthias for the quick fix!