Reputation: 143
Say the JSON response I'm working with is formatted as follows:
[
{
"make": "Tesla",
"model": "Model S",
"year": 2017,
"color": "red",
"owner": "Bob",
"max_speed": 200,
"wheel_size": 30,
"is_convertible": true,
"license": "ABC123",
"cost": 50000,
"down_payment": 2500,
"other_property_1": 1,
"other_property_2": 2,
"other_property_3": 3,
"other_property_4": 4,
"other_property_5": 5,
"other_property_6": 6,
"other_property_7": 7,
"other_property_8": 8,
"other_property_9": 9,
"other_property_10": 10,
"other_property_11": 11
}
]
The JSON here is an array of car objects (just 1 for simplicity), and I am trying to convert this into a model using a JSON Reads converter. Let's say I have a Car case class to represent each object, and that class has has a nested FinancialInfo case class to split up the amount of attributes logically, so to avoid Scala's 22 parameter limit.
import play.api.libs.functional.syntax._
import play.api.libs.json._
case class Car(
make: String,
model: String,
year: Int,
color: String,
owner: String,
maxSpeed: Int,
wheelSize: Int,
isConvertible: Boolean,
license: String,
financialInfo: FinancialInfo, // nested case class to avoid 22 param limit
otherProperty1: Int,
otherProperty2: Int,
otherProperty3: Int,
otherProperty4: Int,
otherProperty5: Int,
otherProperty6: Int,
otherProperty7: Int,
otherProperty8: Int,
otherProperty9: Int,
otherProperty10: Int,
otherProperty11: Int
)
object Car {
implicit val reads: Reads[Car] = (
(__ \ "make").read[String] and
(__ \ "model").read[String] and
(__ \ "year").read[Int] and
(__ \ "color").read[String] and
(__ \ "owner").read[String] and
(__ \ "max_speed").read[Int] and
(__ \ "wheel_size").read[Int] and
(__ \ "is_convertible").read[Boolean] and
(__ \ "license").read[String] and
(__ \ "financialInfo").read[FinancialInfo] and
(__ \ "other_property_1").read[Int] and
(__ \ "other_property_2").read[Int] and
(__ \ "other_property_3").read[Int] and
(__ \ "other_property_4").read[Int] and
(__ \ "other_property_5").read[Int] and
(__ \ "other_property_6").read[Int] and
(__ \ "other_property_7").read[Int] and
(__ \ "other_property_8").read[Int] and
(__ \ "other_property_9").read[Int] and
(__ \ "other_property_10").read[Int] and
(__ \ "other_property_11").read[Int]
)(Car.apply _)
}
case class FinancialInfo(
cost: BigDecimal,
downPayment: BigDecimal
)
object FinancialInfo {
implicit val reads: Reads[FinancialInfo] = (
(__ \ "cost").read[BigDecimal] and
(__ \ "down_payment").read[BigDecimal]
)(FinancialInfo.apply _)
}
However, I'm guessing since there is no property in the JSON called financialInfo
, it is not parsing it correctly. In my real application, I'm getting this error when I use response.json.validate[List[Car]]
:
JsError(List(((0)/financialInfo,List(JsonValidationError(List(error.path.missing),WrappedArray())))))
To summarize, in the example, cost
and down_payment
are not contained in a nested object, even though for the Car case class I had to include a nested model called financialInfo
. What is the best way to work around this error and make sure the values for cost
and down_payment
can be parsed? Any help or insight would be greatly appreciated!
Upvotes: 4
Views: 4527
Reputation: 3964
Reads
can be combined and included into each other.
So, having:
implicit val fiReads: Reads[FinancialInfo] = (
(JsPath \ "cost").read[BigDecimal] and
(JsPath \ "down_payment").read[BigDecimal]
)(FinancialInfo.apply _)
We can include it into the parent Reads
:
implicit val carReads: Reads[Car] = (
(JsPath \ "make").read[String] and
(JsPath \ "model").read[String] and
fiReads // <--- HERE!
)(Car.apply _)
Now, with the following JSON:
private val json =
"""
|[
| {
| "make": "Tesla",
| "model": "Model S",
| "cost": 50000,
| "down_payment": 2500
| },
| {
| "make": "Tesla",
| "model": "Model D",
| "cost": 30000,
| "down_payment": 1500
| }
|]
""".stripMargin
val parsedJsValue = Json.parse(json)
val parsed = Json.fromJson[List[Car]](parsedJsValue)
println(parsed)
It is parsed properly:
JsSuccess(List(Car(Tesla,Model S,FinancialInfo(50000,2500)), Car(Tesla,Model D,FinancialInfo(30000,1500))),)
p.s. The Reads
in the original question do no need to be wrapped into different object
s. Related implicit values would be better inside same scope, closer to where they are actually used.
Upvotes: 7