I want to create a case class which can incorporate a record of string and another case class entity.
For example:
case class Student(
name: String
age: Int
)
case class Example(
[key:String]: Student
)
Now I want to use Example
to add multiple attributes where attribute
could have N number of elements however the type of all those attributes would remain Student. Here's an example:
Example(student1 = Student("name",12),student2=Student("name2",13))
Reason why I am using Case class is that I need to transform this into a JSON using UPickle library and so I wanted to know on the feasibility of achieving the same.
Please note that Example
class not just contains [key:String]: Student
attribute types but also somethings like:
case class Example(
[key:String]: Student,
_logOp: Option[Boolean] = false,
queryName: String,
...
)
The transformed result for case class:
case class Example(
_logOp: String,
variation: String,
[key:String]: FiltersCaseClass
/* This line I have added to simplify and make my problem more understandable. Basically the case class would contain some properties like `_logOp` `variation` and then a lot of keys with their values as another case class `FilterCaseClass`
*/
)
should look something like this:
{"_logOp":"AND","variation": "en","ids": {"_logOp": "OR","_expressions": [{"value": "242424"},{"value": "242422"}]}}
where FilterCaseClass is:
case class FilterCaseClass(
_logOp: String,
_expressions: Seq[SingleValueFilter]
)
where SingleValueFilter is another case class containing values
Edit 1:
As per one of the answers by Dymtro:
case class Example(
m: Map[String, Student],
_logOp: Option[Boolean] = Some(false),
queryName: String
)
object Example {
implicit val rw: ReadWriter[Example] = macroRW
}
write(Example(
Map("student1" -> Student("name",12), "student2" -> Student("name2",13)),
Some(true),
"abc"
))
//{"m":{"student1":{"name":"name","age":12},"student2":{"name":"name2","age":13}},"_logOp":[true],"queryName":"abc"}
The only difference I want here is:
{"student1":{"name":"name","age":12},"student2":{"name":"name2","age":13},"_logOp":[true],"queryName":"abc"}
The difference is that I want case class to be flexible to add key value pairs of Student class.
CodePudding user response:
You don't need a case class Example
, in µPickle you can create json mixing manual construction and case-class construction
import upickle.default.{macroRW, ReadWriter, write} // "com.lihaoyi" %% "ujson" % "0.9.6"
case class Student(
name: String,
age: Int
)
object Student {
implicit val rw: ReadWriter[Student] = macroRW
}
ujson.Obj("student1" -> write(Student("name",12)), "student2" -> write(Student("name2",13)))
//{"student1":"{\"name\":\"name\",\"age\":12}","student2":"{\"name\":\"name2\",\"age\":13}"}
If [key:String]: Student
means Map[String, Student]
then µPickle seems to support this out-of-the-box
case class Example(
m: Map[String, Student],
_logOp: Option[Boolean] = Some(false),
queryName: String
)
object Example {
implicit val rw: ReadWriter[Example] = macroRW
}
write(Example(
Map("student1" -> Student("name",12), "student2" -> Student("name2",13)),
Some(true),
"abc"
))
//{"m":{"student1":{"name":"name","age":12},"student2":{"name":"name2","age":13}},"_logOp":[true],"queryName":"abc"}
It shouldn't be nested within
m
You can achieve this with a custom codec (pickler)
import upickle.default.{ReadWriter, macroRW, readwriter, transform, write, read}
import scala.collection.mutable
case class Example(
m: Map[String, Student],
_logOp: Option[Boolean] = Some(false),
queryName: String
)
object Example {
implicit val rw: ReadWriter[Example] = {
val standardExampleRW = macroRW[Example]
readwriter[ujson.Value].bimap[Example](
example => transform[Example](example)(standardExampleRW).to[ujson.Value] match {
case ujson.Obj(standardMap) =>
val newMap = mutable.LinkedHashMap.empty[String, ujson.Value]
standardMap.remove("m")
newMap.addAll(example.m.map { case (str, stud) => str -> transform[Student](stud).to[ujson.Value]})
.addAll(standardMap)
ujson.Obj(newMap)
},
// if you don't need a reversed transform i.e. from a json to an Example then you can omit this part
// _ => ???
{
case ujson.Obj(newMap) =>
val logOpJson = newMap.remove("_logOp")
val logOp = logOpJson.map(transform[ujson.Value](_).to[Option[Boolean]])
val queryNameJson = newMap.remove("queryName")
val queryName = queryNameJson.map(transform[ujson.Value](_).to[String]).getOrElse("")
val m = newMap.map { case (str, json) => str -> transform[ujson.Value](json).to[Student] }.toMap
logOp.map(Example(m, _, queryName)).getOrElse(Example(m, queryName = queryName))
}
)
}
}
write(Example(
Map("student1" -> Student("name",12), "student2" -> Student("name2",13)),
Some(true),
"abc"
))
//{"student1":{"name":"name","age":12},"student2":{"name":"name2","age":13},"_logOp":[true],"queryName":"abc"}
read[Example](
"""{"student1":{"name":"name","age":12},"student2":{"name":"name2","age":13},"_logOp":[true],"queryName":"abc"}"""
)
//Example(Map(student1 -> Student(name,12), student2 -> Student(name2,13)),Some(true),abc)
So, basically you can generate case classes in Scala but it's not necessary for serialization into json format.