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

Inline Json Codec #1078

Open
saadaamer opened this issue Sep 4, 2023 · 1 comment
Open

Inline Json Codec #1078

saadaamer opened this issue Sep 4, 2023 · 1 comment
Labels

Comments

@saadaamer
Copy link

Hello,

We are in a process of migrating from play json to jsoniter-scala and struggling to find solution for the following scenario

Following is the example model and the json generated

case class Name(firstName : String, middleName : String, lastName : String)

case class ContactDetail(email : String, name : Name)

object ContactDetails {

  implicit val nameCodec : JsonValueCodec[Name] = JsonCodecMaker.make[Name]
  implicit val contactDetailCodec : JsonValueCodec[ContactDetail] = JsonCodecMaker.make[ContactDetail]
}

  val contactDetails = ContactDetail("[email protected]", Name("firstName", "middleName", "lastName"))
  println(s"Json ${writeToString(contactDetails)}")

  {"email":"[email protected]","name": {"firstName":"firstName","middleName":"middleName","lastName":"lastName"}}

In play-json we can inline the above json like following

  {"email":"[email protected]", "firstName":"firstName","middleName":"middleName","lastName":"lastName"}

is there a way/configuration I can use to achieve the above in Jsoniter?

@plokhotnyuk
Copy link
Owner

plokhotnyuk commented Sep 5, 2023

Hi, @saadaamer ! Thanks for trying jsoniter-scala and sending your feedback!

Unfortunately, an automatic unwrapping works only for AnyVal instances.

I've created a separated issue for adding that configuration for other classes: #1079

But you can use the following custom codec with an auxiliary case class to serialize nested Name unwrapped:

object ContactDetails {
  private case class ContactDetailUnwrapped(email: String, firstName: String, middleName: String, lastName: String)

  private object ContactDetailUnwrapped {
    def toContactDetail(x: ContactDetailUnwrapped): ContactDetail =
      if (x eq null) null
      else ContactDetail(x.email, Name(x.firstName, x.middleName, x.lastName))

    def fromContactDetail(x: ContactDetail): ContactDetailUnwrapped =
      if (x eq null) null
      else {
        val name = x.name
        ContactDetailUnwrapped(x.email, name.firstName, name.middleName, name.lastName)
      }
  }

  implicit val contactDetailCodec: JsonValueCodec[ContactDetail] = new JsonValueCodec[ContactDetail] {
    private[this] val codec: JsonValueCodec[ContactDetailUnwrapped] = JsonCodecMaker.make[ContactDetailUnwrapped]

    override def decodeValue(in: JsonReader, default: ContactDetail): ContactDetail =
      ContactDetailUnwrapped.toContactDetail(codec.decodeValue(in, ContactDetailUnwrapped.fromContactDetail(default)))

    override def encodeValue(x: ContactDetail, out: JsonWriter): Unit =
      codec.encodeValue(ContactDetailUnwrapped.fromContactDetail(x), out)

    override def nullValue: ContactDetail = null
  }
}

If you would want more efficient codec implementation without instantiation of auxiliary instances then an easier way to write it is printing the codec generated for ContactDetailUnwrapped (by adding implicit val printCodec: CodecMakerConfig.PrintCodec = new CodecMakerConfig.PrintCodec {} before private[this] val codec: JsonValueCodec[ContactDetailUnwrapped] ...) and modifying it to instantiate ContactDetail and Name during parsing immediately without using intermediate instance of ContactDetailUnwrapped for default and result values.

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

2 participants