Reputation: 266910
I have a collection of objects like:
class Car(val id: Int, val name: String)
I will then have a collection of cars in a List
List[Car]();
I want to be able to convert the List[Car] into a JSON string, and then load it from a JSON string back.
As a side note, how volatile is this conversion process, say I add another property to my User object? (Note: I don't want an answer that solves this issue, just wondering if adding an item would break it)
Update
How to handle Maps correctly like: Map[Int, Long]
Say my model is updated with the Map included:
class Car(val id: Int, val name: String, val options: Map[Int, Long])
Using play's json I create my Writes implicit like:
implicit val carWrites = new Writes[Car] {
def writes(c: Car) = Json.obj{
"id" -> c.id
"name" -> c.name
//"options" -> t.options
}
}
Currently I see an error: No Json serializer found for type Map[Int,Long]
Upvotes: 0
Views: 219
Reputation: 278
case class Car(id: Int, name: String, options: Map[Int, Long])
object Car{
implicit val carWrites = new Writes[Car] {
def writes(c: Car) = Json.obj(
"id" -> c.id,
"name" -> c.name,
"options" -> c.options.map(item=>{
Json.obj(
"id" -> item._1,
"name" -> item._2
)
})
)
}
implicit val carReads =
(__ \ "id").read[Int] ~
(__ \ "name").read[String] ~
(__ \ "options").read(
(__ \ "id").read[Int] ~
(__ \ "name").read[Long]
)
(Car)
}
And then to output it, if using Play:
val x = Car(1,"Porsche",Map(0->1,1->2.toLong))
val json = Json.toJson(x)
Ok(json).as("application/json")
Upvotes: 1
Reputation: 1988
If you're using Play framework then
case class User(val id: Int, val name: String, val age: Int)
implicit val userReads : Reads[User] = (
(__ \ "id").read[Int] ~
(__ \ "name").read[String] ~
(__ \ "age").read[Int]
)(User.apply _)
play.api.libs.json.Json.fromJson[User](incomingJson)
Similarly define Writes[User]
Upvotes: 1
Reputation: 1544
With the play-json library (available as standalone library) you are free to add fields to the User class without even needing to modify the play-json Reads/Writes/Formats that are used to serialize/deserialize, e.g. of type Int, String etc. However if you add a custom type then you'd need to write a custom serializer for that field of course.
If you want to make changes that are backwards compatible with clients unaware of the new fields then set them as type Option and there should be no need to do anything more.
This would work even if a client sends JSON without the customField:
class User(val id: Int, val name: String, val age: Int, val customField: Option[String])
However this would fail if client does not include customField (Play JSON does not set customField to null as would be done say by Gson for Java):
class User(val id: Int, val name: String, val age: Int, val customField: String)
Cheers,
Upvotes: 1
Reputation: 4182
Are you using any existing toolkits to do this? I've got experience with Spray-Json. You don't have to use the entire Spray toolkit, you can just use Spray-Json by itself. Your marshalling would be simple.
object UserJsonProtocol extends DefaultJsonProtocol {
implicit val userFormat = jsonFormat3(User)
}
import UserJsonProtocol._
val jsonFromList = listUsers.toJson
val usersFromJsonString = usersString.parseJson.convertTo[List[User]]
Adding a field to the User class only requires updating the jsonFormat to the correct number of fields.
Upvotes: 1