jackson-module-scala
jackson-module-scala copied to clipboard
Nested sequence of polymorphic type serialized without type info (ver 2.10.2)
The following code works as expected
import com.fasterxml.jackson.annotation.{JsonSubTypes, JsonTypeInfo}
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.scala.DefaultScalaModule
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
@JsonSubTypes(Array(
new JsonSubTypes.Type(value = classOf[Ingredient.Meat], name = "meat"),
new JsonSubTypes.Type(value = classOf[Ingredient.Vegetable], name = "veggie")
))
trait Ingredient
object Ingredient {
case class Meat(of: String) extends Ingredient
case class Vegetable(name: String) extends Ingredient
}
case class Burrito(filling: Seq[Ingredient])
case class BurritoTray(content: Seq[Burrito])
object Main extends App {
val mapper = new ObjectMapper().registerModule(DefaultScalaModule)
println(mapper.writeValueAsString(BurritoTray(Seq(
Burrito(Seq(Ingredient.Meat("chicken"), Ingredient.Vegetable("salad"))),
Burrito(Seq(Ingredient.Vegetable("salad"), Ingredient.Vegetable("pickles")))
))))
}
printing the following output
{"content":[{"filling":[{"type":"meat","of":"chicken"},{"type":"veggie","name":"salad"}]},{"filling":[{"type":"veggie","name":"salad"},{"type":"veggie","name":"pickles"}]}]}
But if the Burrito class is replaced by a simple Seq[Ingredient]
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
@JsonSubTypes(Array(
new JsonSubTypes.Type(value = classOf[Ingredient.Meat], name = "meat"),
new JsonSubTypes.Type(value = classOf[Ingredient.Vegetable], name = "veggie")
))
trait Ingredient
object Ingredient {
case class Meat(of: String) extends Ingredient
case class Vegetable(name: String) extends Ingredient
}
case class BurritoTray(content: Seq[Seq[Ingredient]])
object Main extends App {
val mapper = new ObjectMapper().registerModule(DefaultScalaModule)
println(mapper.writeValueAsString(BurritoTray(Seq(
Seq(Ingredient.Meat("chicken"), Ingredient.Vegetable("salad")),
Seq(Ingredient.Vegetable("salad"), Ingredient.Vegetable("pickles"))
))))
}
the type information is omitted in the resulting json
{"content":[[{"of":"chicken"},{"name":"salad"}],[{"name":"salad"},{"name":"pickles"}]]}
This issue is not present in equivalent java code
public class JacksonNestedCollection {
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
@JsonSubTypes({
@JsonSubTypes.Type(value = Meat.class, name = "Meat"),
@JsonSubTypes.Type(value = Veggie.class, name = "Veggie")
})
private interface Ingredient {}
private static class Meat implements Ingredient {
public final String of;
public Meat(String of) {
this.of = of;
}
}
private static class Veggie implements Ingredient {
public final String name;
public Veggie(String name) {
this.name = name;
}
}
private static final class BurritoTray {
public final List<List<Ingredient>> content;
public BurritoTray(List<List<Ingredient>> content) {
this.content = content;
}
}
public static void main(String[] args) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.writeValueAsString(new BurritoTray(Arrays.asList(
Arrays.asList(new Meat("chicken"), new Veggie("salad")),
Arrays.<Ingredient>asList(new Veggie("salad"), new Veggie("pickles"))
))));
}
}
I think similar symptom also occurs to Tuple.
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT)
class PyContextMetaType
case class Wpbp (sepInd: Int
, esgGrping: Int
, psaGrping: Int
, pernr: BigInt
, eeGrp: Char
, eeSubGrp: String
, perArea: String
, perSubArea: String
, orgUnit: String
, position: String
, costCenter: String) extends PyContextMetaType
case class LogNode2(var text: String,
@JsonBackReference
dadNode: Option[AnyRef] = None,
var data: (Option[PyContextMetaType],Option[PyContextMetaType]) = (None, None),
@JsonManagedReference
children: ListBuffer[LogNode] = new ListBuffer[LogNode],
detailText: String = "") extends CborSerializable
{
....
}
object Main3 {
def main(args: Array[String]): Unit = {
val wpbp = Wpbp(1,3,3,28000001,1,"A0","CN01","SH01","HR Dept","","CC1234")
val wpbpNode2 = LogNode2("WPBP", None, (None, Some(wpbp)))
val mapper = new ObjectMapper() with ScalaObjectMapper
mapper.registerModule(DefaultScalaModule)
val jsonStr = mapper.writeValueAsString(wpbpNode2)
println(jsonStr)
}
}
Pay attention to the "data" member of LogNode2, which is a Tuple2 that uses PyContextMetaType. The serialized jsonStr lost the type info of Wpbp object:
{
"text": "WPBP",
"data": [
null,
{
"sepInd": 1,
"esgGrping": 3,
"psaGrping": 3,
"pernr": 28000001,
"eeGrp": "\u0001",
"eeSubGrp": "A0",
"perArea": "CN01",
"perSubArea": "SH01",
"orgUnit": "HR Dept",
"position": "",
"costCenter": "CC1234"
}
],
"children": [],
"detailText": ""
}
The expected jsonStr should be:
{
"text": "WPBP",
"data": [
null,
{
"Wpbp": {
"sepInd": 1,
"esgGrping": 3,
"psaGrping": 3,
"pernr": 28000001,
"eeGrp": "\u0001",
"eeSubGrp": "A0",
"perArea": "CN01",
"perSubArea": "SH01",
"orgUnit": "HR Dept",
"position": "",
"costCenter": "CC1234"
}
}
],
"children": [],
"detailText": ""
}
@tremaluca
Hi there,
Were you able to find a workaround for this?
Hi @iLoveZod13, I also had some different issues with tuples (ids not properly serialized), and I solved by not using tuples, replacing them with a simple "Pair" case class...
Hi @tremaluca Yes I noticed your Pair case class workaround in issue 443, and I am trying that now. Thanks a lot for the enlightening.