You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@fory.apache.org by "pjfanning (via GitHub)" <gi...@apache.org> on 2026/09/30 16:07:54 UTC

[I] [Scala] Native mode rebuilds IntMap, LongMap, BitSet, mutable.LongMap and StringBuilder as a different collection class [fory]

pjfanning opened a new issue, #4102:
URL: https://github.com/apache/fory/issues/4102

   ### Search before asking
   
   - [x] I had searched in the issues and found no similar issues.
   
   ### Version
   
   `main` (1.8.0-SNAPSHOT), Scala 2.13.18 and 3.3.8
   
   ### Component(s)
   
   Scala
   
   ### Minimal reproduce step
   
   ```scala
   import org.apache.fory.scala.ForyScala
   import scala.collection.immutable
   
   case class Holder(m: immutable.IntMap[String])
   
   val fory = ForyScala.builder()
     .withXlang(false)
     .withRefTracking(true)
     .requireClassRegistration(false)
     .build()
   
   fory.deserialize(fory.serialize(Holder(immutable.IntMap(1 -> "a"))))
   // DeserializationException caused by
   // ClassCastException: scala.collection.immutable.Map$Map1 cannot be cast to scala.collection.immutable.IntMap
   ```
   
   ### What did you expect to see?
   
   A native-mode round trip should rebuild the same collection class, as it already does for `List`, `Vector`, `HashMap`, `TreeMap`, `LinkedHashMap` and others.
   
   ### What did you see instead?
   
   The native Scala collection serializers write the value's generic factory, `iterableFactory`, `mapFactory` or `sortedIterableFactory`. For these specialised kinds that factory builds a different class. The same happens in compatible and schema-consistent modes:
   
   | Written | Read back as | As a declared case class field |
   | --- | --- | --- |
   | `immutable.IntMap` | `immutable.Map$Map1` / `Map` | `ClassCastException` |
   | `immutable.LongMap` | `immutable.Map$Map1` / `Map` | `ClassCastException` |
   | `mutable.BitSet` | `mutable.TreeSet` | `ClassCastException` |
   | `mutable.LongMap` | `mutable.HashMap` | `ClassCastException` |
   | `mutable.StringBuilder` | `mutable.ArrayBuffer[Char]` | `ClassCastException` |
   | `immutable.ArraySeq[Int]` (`ArraySeq$ofInt`) | `ArraySeq$ofRef` (boxed) | works, but loses the unboxed representation |
   
   As a root value, the decoded collection compares equal to the original, so the change goes unnoticed until a typed field or a pattern match depends on the concrete class.
   
   ### Anything Else?
   
   The factories are chosen in `ScalaCollectionSerializer`, `ScalaSeqSerializer`, `ScalaSortedSetSerializer` and `ScalaMapSerializer` (`CollectionSerializer.scala` and `MapSerializer.scala`). These kinds probably need their specific factory or a dedicated serializer, for example `IntMap`/`LongMap` factories, `BitSet` from its bitmask, `StringBuilder` as a string, and `ArraySeq` keeping its element `ClassTag`.
   
   Related: #4101 covers the registration gap for several of the same types. Found while adding tests in #4100.
   
   ### Are you willing to submit a PR?
   
   - [x] I'm willing to submit a PR!
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fory.apache.org.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@fory.apache.org
For additional commands, e-mail: commits-help@fory.apache.org