FasterXML / jackson-module-scala

Add-on module for Jackson (https://github.com/FasterXML/jackson) to support Scala-specific datatypes
Apache License 2.0
502 stars 142 forks source link

support deserializing Scala Iterators #639

Open pjfanning opened 1 year ago

pjfanning commented 1 year ago

This is open just for discussion. jackson-module-scala can serialize Iterators but will fail if you try to deserialize them.

Noone has complained yet.

Most users would use standard collections when it comes to supporting Array-like data types.

Example test:

package com.fasterxml.jackson.module.scala.deser

import com.fasterxml.jackson.module.scala.DefaultScalaModule
import com.fasterxml.jackson.module.scala.introspect.ScalaAnnotationIntrospectorModule
import org.scalatest.BeforeAndAfterEach

object IteratorWithNumberDeserializerTest {
  case class IteratorLong(longs: Iterator[Long])
  case class IteratorJavaLong(longs: Iterator[java.lang.Long])
  case class IteratorBigInt(longs: Iterator[BigInt])
}

class IteratorWithNumberDeserializerTest extends DeserializerTest with BeforeAndAfterEach {
  lazy val module: DefaultScalaModule.type = DefaultScalaModule
  import IteratorWithNumberDeserializerTest._

  private def sumIteratorLong(v: Iterator[Long]): Long = v.sum
  private def sumIteratorJavaLong(v: Iterator[java.lang.Long]): Long = v.map(_.toLong).sum
  private def sumIteratorBigInt(v: Iterator[BigInt]): Long = v.sum.toLong

  "JacksonModuleScala" should "deserialize IteratorLong" in {
    ScalaAnnotationIntrospectorModule.registerReferencedValueType(classOf[IteratorLong], "longs", classOf[Long])
    try {
      val v1 = deserialize("""{"longs":[151,152,153]}""", classOf[IteratorLong])
      v1 shouldBe IteratorLong(Iterator(151L, 152L, 153L))
      //this will next call will fail with a Scala unboxing exception unless you ScalaAnnotationIntrospectorModule.registerReferencedValueType
      //or use one of the equivalent classes in SeqWithNumberDeserializerTest
      sumIteratorLong(v1.longs) shouldBe 456L
    } finally {
      ScalaAnnotationIntrospectorModule.clearRegisteredReferencedTypes()
    }
  }

  it should "deserialize IteratorJavaLong" in {
    val v1 = deserialize("""{"longs":[151,152,153]}""", classOf[IteratorJavaLong])
    v1 shouldBe IteratorJavaLong(Iterator(151L, 152L, 153L))
    sumIteratorJavaLong(v1.longs) shouldBe 456L
  }

  it should "deserialize IteratorBigInt" in {
    val v1 = deserialize("""{"longs":[151,152,153]}""", classOf[IteratorBigInt])
    v1 shouldBe IteratorBigInt(Iterator(151L, 152L, 153L))
    sumIteratorBigInt(v1.longs) shouldBe 456L
  }
}

Exception looks like:

Cannot find a Value deserializer for abstract type [collection-like type; class scala.collection.Iterator, contains [simple type, class long]]
 at [Source: (String)"{"longs":[151,152,153]}"; line: 1, column: 1]
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot find a Value deserializer for abstract type [collection-like type; class scala.collection.Iterator, contains [simple type, class long]]
cowtowncoder commented 1 year ago

I think Java-side behavior is similar; Iterator mostly expected to work for serialization. Although technically one could quite easily support deserialization (into something to obtain iterator from), not much benefit to do before someone has use case. But good to have this issue so users can comment etc.