@@ -4,7 +4,11 @@ import org.json4s.ext.{JavaTypesSerializers, JodaTimeSerializers}
44import org .json4s .jackson .Serialization
55import org .json4s ._
66
7+ import java .text .SimpleDateFormat
8+ import java .time .{Instant , LocalDate , LocalDateTime }
9+ import java .util .Date
710import scala .language .implicitConversions
11+ import scala .reflect .ClassTag
812
913/** Created by smanciot on 14/05/2020.
1014 */
@@ -21,7 +25,8 @@ package object serialization {
2125 implicit def map2String (data : Map [String , Any ])(implicit formats : Formats ): String =
2226 serialization.write(data)
2327
24- val defaultExcludedFields = List (" serialVersionUID" , " __serializedSizeCachedValue" , " bitmap$0" )
28+ val defaultExcludedFields : List [String ] =
29+ List (" serialVersionUID" , " __serializedSizeCachedValue" , " bitmap$0" )
2530
2631 def caseClass2Map (
2732 cc : AnyRef
@@ -64,4 +69,67 @@ package object serialization {
6469 */
6570 implicit def option2String (opt : Option [String ]): String = opt.getOrElse(" " )
6671
72+ import scala .reflect .runtime .universe ._
73+ import scala .reflect .runtime .{currentMirror => cm }
74+
75+ def isCaseClass [T : TypeTag ](paramType : Type ): Boolean = {
76+ paramType.typeSymbol.isClass && paramType.typeSymbol.asClass.isCaseClass
77+ }
78+
79+ def updateCaseClass [T : TypeTag ](obj : T , updates : Map [String , Any ])(implicit
80+ tag : ClassTag [T ]
81+ ): T = {
82+ val classType = typeOf[T ].typeSymbol.asClass
83+ val classMirror = cm.reflect(obj)
84+
85+ // Get the primary constructor of the case class
86+ val constructor = typeOf[T ].decl(termNames.CONSTRUCTOR ).asMethod
87+ val classInstanceMirror = cm.reflectClass(classType)
88+
89+ // Get the parameters of the constructor
90+ val params = constructor.paramLists.flatten
91+
92+ // Build new parameter values (updated if in the map, or original if not)
93+ val newArgs = params.map { param =>
94+ val paramName = param.name.toString
95+ val paramType = param.typeSignature
96+ updates.get(paramName) match {
97+ case Some (newValue) =>
98+ // Handle type conversion based on the expected parameter type
99+ (paramType, newValue) match {
100+ case (t, v : String ) if t =:= typeOf[Boolean ] =>
101+ v.toBoolean // Convert string to Boolean
102+ case (t, v : String ) if t =:= typeOf[Date ] =>
103+ new SimpleDateFormat ().parse(v) // Convert string to Date
104+ case (t, v : String ) if t =:= typeOf[Double ] =>
105+ v.toDouble // Convert string to Double
106+ case (t, v : String ) if t =:= typeOf[Float ] =>
107+ v.toFloat // Convert string to Float
108+ case (t, v : String ) if t =:= typeOf[Instant ] =>
109+ Instant .parse(v) // Convert string to Instant
110+ case (t, v : String ) if t =:= typeOf[Int ] =>
111+ v.toInt // Convert string to Int
112+ case (t, v : String ) if t =:= typeOf[LocalDate ] =>
113+ LocalDate .parse(v) // Convert string to LocalDate
114+ case (t, v : String ) if t =:= typeOf[LocalDateTime ] =>
115+ LocalDateTime .parse(v) // Convert string to LocalDateTime
116+ case (t, v : String ) if t =:= typeOf[Long ] =>
117+ v.toLong // Convert string to Long
118+ case (t, v : String ) if t =:= typeOf[Short ] =>
119+ v.toShort // Convert string to Short
120+ case (t, v : Map [String , Any ]) if isCaseClass(t) =>
121+ updateCaseClass(classMirror.reflectField(t.decl(TermName (paramName)).asTerm).get, v)
122+ case _ =>
123+ newValue // If types match or are already compatible, use the value directly
124+ }
125+ case None =>
126+ // Reflectively get the current field value from the case class instance
127+ val fieldTerm = typeOf[T ].decl(TermName (paramName)).asTerm
128+ classMirror.reflectField(fieldTerm).get
129+ }
130+ }
131+
132+ // Create a new instance of the case class using the updated arguments
133+ classInstanceMirror.reflectConstructor(constructor)(newArgs : _* ).asInstanceOf [T ]
134+ }
67135}
0 commit comments