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

Bidirectional mapping #96

Closed
lloydmeta opened this issue Nov 30, 2018 · 3 comments · Fixed by #547
Closed

Bidirectional mapping #96

lloydmeta opened this issue Nov 30, 2018 · 3 comments · Fixed by #547
Labels
enhancement relatively easy but not small Task with requires some knowlege and writing more code, but not the intimate knoledge about macros

Comments

@lloydmeta
Copy link

Sometimes if you have two classes, it might be nice to be able to define a single bi-directional transform instead of having to write one for each one

// To send to your fancy no-sql store; fixed Schema
case class PersistedUser(name: String, address: String) 

// To send to your users
case class ApiUser(name: String, streetAddress: String)

// Declare a 2 way transformer
val biTransformer = BiTransform[PersistedUser, ApiUser]
  .withFieldRenamed(_.address, _.streetAdress)

val p = PersistedUser("Coookie", "Sesame Street")
val a = biTransformer.from(p)
val pAgain = biTransformer.from(a)

Not sure if/how this could be done, but just a thought that was inspired somewhat by play-json's `Format, which acts as both an encoder and a decoder, coupled to their functional combinator syntax, which comes together to allow for this sort of thing:

// Bi-directional formatter with renaming.
val locationFormat: Format[Location] = (
      (JsPath \ "lat").format[Double] and
      (JsPath \ "long").format[Double]
    )(Location.apply, unlift(Location.unapply))
Actual Ammonite-usage of play-json
14:00 $ amm
Loading...
Welcome to the Ammonite Repl 1.0.5
(Scala 2.12.4 Java 1.8.0_152)
If you like Ammonite, please support our development at www.patreon.com/lihaoyi
@ import $ivy.{`com.typesafe.play::play-json:2.6.10`}
import $ivy.$

@ import play.api.libs.json._, play.api.libs.functional.syntax._
import play.api.libs.json._, play.api.libs.functional.syntax._

@ case class Location(latitude: Double, longitude: Double)
defined class Location

@ implicit val locationFormat: Format[Location] = (
      (JsPath \ "lat").format[Double] and
      (JsPath \ "long").format[Double]
    )(Location.apply, unlift(Location.unapply))
locationFormat: Format[Location] = play.api.libs.json.OFormat$$anon$1@519e67e

@ val l = Location(123d, 123d)
l: Location = Location(123.0, 123.0)

@ val j = Json.toJson(l)
j: JsValue = JsObject(Map("lat" -> JsNumber(123.0), "long" -> JsNumber(123.0)))

@ val lAgain = j.as[Location]
lAgain: Location = Location(123.0, 123.0)

It seems to me like doing something like that in Chimney would be "even better" because we don't need to mess around with a JsPath and things are fully typed.

@yoohaemin
Copy link
Contributor

Maybe you can utilize isomorphisms from other libraries (like monocle.Iso), and define two implicit Transformer instances based on that?

@ghostdogpr
Copy link
Contributor

Also interested in this kind of feature. When you need transformation both ways, it would be nice to be able to define the field mapping only once.

@MateuszKubuszok MateuszKubuszok added the low hanging fruit Task that should be easy to implement - perfect for people that want to start contributing. label Jul 24, 2023
@MateuszKubuszok MateuszKubuszok added relatively easy but not small Task with requires some knowlege and writing more code, but not the intimate knoledge about macros and removed low hanging fruit Task that should be easy to implement - perfect for people that want to start contributing. labels Oct 20, 2023
@MateuszKubuszok
Copy link
Member

MateuszKubuszok commented Oct 23, 2023

If anyone is interested in implementing such thing, it would require:

  • creating some case classes, let's call them
    case class IsoTransformer[L, R](toRight: Transformer[L, R], toLeft: Transformer[R, L])
    case class CodecTransformer[Domain, DTO](encode: Transformer[Domain, DTO], decode: PartialTransformer[DTO, Domain])
  • creating IsoTransformerDefinition[L, R] and CodecTransformerDefinition[Domain, DTO] similar to TransformerDefinition and PartialTransformerDefinition
  • providing macros similar to existing macros on 2 & 3
    • it can be pretty much done with copy-paste-edit
  • providing IsoTransformer*Spec and CodecTransformer*Spec for: ImplicitResolution, JavaBean, Product, Sealed, ValueType etc

All in all, it is not a difficult work, so most new contributors should be able to do it, but it is also not a one liner to implement during afternoon.

@MateuszKubuszok MateuszKubuszok linked a pull request May 30, 2024 that will close this issue
9 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement relatively easy but not small Task with requires some knowlege and writing more code, but not the intimate knoledge about macros
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants