Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Objects in tuples are not serialized as ids #443

Closed
tremaluca opened this issue Mar 9, 2020 · 4 comments
Closed

Objects in tuples are not serialized as ids #443

tremaluca opened this issue Mar 9, 2020 · 4 comments
Assignees
Labels

Comments

@tremaluca
Copy link

Using classes annotated with JsonIdentityInfo inside of tuples has a weir behaviour

@JsonIdentityInfo(generator = classOf[ObjectIdGenerators.IntSequenceGenerator])
case class Foo(x: Int)

case class ManyFooPairs(foos: Seq[(Foo, Foo)])

object Main extends App {
  val mapper = new ObjectMapper().registerModule(DefaultScalaModule)
  val f1 = Foo(1)
  val f2 = Foo(2)
  println(mapper.writeValueAsString(
    ManyFooPairs(Seq((f1, f1), (f1, f2)))
  ))
}

prints

{"foos":[[{"@id":1,"x":1},{"@id":1,"x":1}],[{"@id":1,"x":1},{"@id":1,"x":2}]]}

Introducing a specific pair class solves the issue:

@JsonIdentityInfo(generator = classOf[ObjectIdGenerators.IntSequenceGenerator])
case class Foo(x: Int)

case class FooPair(l: Foo, r: Foo)

case class ManyFooPairs(foos: Seq[FooPair])

object Main extends App {
  val mapper = new ObjectMapper().registerModule(DefaultScalaModule)
  val f1 = Foo(1)
  val f2 = Foo(2)
  println(mapper.writeValueAsString(
    ManyFooPairs(Seq(FooPair(f1, f1), FooPair(f1, f2)))
  ))
}

prints

{"foos":[{"l":{"@id":1,"x":1},"r":1},{"l":1,"r":{"@id":2,"x":2}}]}
@pjfanning
Copy link
Member

pjfanning commented Sep 24, 2021

This still behaves as described

@pjfanning
Copy link
Member

pjfanning commented Sep 27, 2021

@cowtowncoder the ObjectId code seems to work at the SerializationProvider level - so that if the same SerializationProvider instance sees the same class instance twice, then the second instance gets the same id assigned.

In the example in the issue description, when Foo is wrapped in a class FooPair, the same SerializationProvider instance serializes all the Foo instances. When Foo is wrapped in a Tuple, we seem to end up with different SerializationProvider instances serializing each Foo instance - so the ids all end up as 1.

The Tuple Serializer in jackson-modue-scala looks like this

  def serialize(value: Product, jgen: JsonGenerator, provider: SerializerProvider): Unit = {
    jgen.writeStartArray()
    value.productIterator.foreach(jgen.writeObject _)
    jgen.writeEndArray()
  }

So it ignores the provider that is passed in and when jgen.writeObject is called, ultimately, a new provider will need to be created. I can't see a way to provide jgen.writeObject with a pre-created provider. Would you have any suggestions?

@pjfanning
Copy link
Member

This seems to be the fix for the Tuple Serializer:

  def serialize(value: Product, jgen: JsonGenerator, provider: SerializerProvider): Unit = {
    jgen.writeStartArray()
    value.productIterator.foreach(provider.writeValue(jgen, _))
    jgen.writeEndArray()
  }

@pjfanning pjfanning added the 2.13 label Sep 27, 2021
@pjfanning pjfanning self-assigned this Sep 27, 2021
@cowtowncoder
Copy link
Member

Right, without seeing the original code I would speculate that something is calling different underlying ObjectMapper instance -- SerializationProvider is constructed by mapper when writeValue() (and related) is called.
This is why it is generally preferable to call methods either through provided SerializerProvider or JsonGenerator directly. Jackson 3.0 does actually improve things a lot as well wrt use of JsonGenerator.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants