简体   繁体   English

使用Scala / Play将JSON转换为带有嵌套对象的case类

[英]Convert JSON to case class with a nested objects using Scala/Play

Say the JSON response I'm working with is formatted as follows: 说我正在使用的JSON响应格式如下:

[
    {
       "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. 这里的JSON是一个汽车对象数组(为简单起见只有1),我试图使用JSON Reads转换器将其转换为模型。 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. 假设我有一个Car case类来表示每个对象,并且该类有一个嵌套的FinancialInfo case类,用于逻辑分割属性的数量,以避免Scala的22参数限制。

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. 但是,我猜测因为JSON中没有名为financialInfo属性,所以它没有正确解析它。 In my real application, I'm getting this error when I use response.json.validate[List[Car]] : 在我的实际应用程序中,当我使用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 . 总而言之,在示例中, costdown_payment不包含在嵌套对象中,即使对于Car case类,我必须包含一个名为financialInfo的嵌套模型。 What is the best way to work around this error and make sure the values for cost and down_payment can be parsed? 解决此错误的最佳方法是什么,并确保可以解析costdown_payment的值? Any help or insight would be greatly appreciated! 任何帮助或见解将不胜感激!

Reads can be combined and included into each other. Reads可以组合并包含在一起。

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 : 我们可以将它包含在父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: 现在,使用以下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))),)

ps The Reads in the original question do no need to be wrapped into different object s. ps原始问题中的Reads不需要包含在不同的object Related implicit values would be better inside same scope, closer to where they are actually used. 相关隐含值在相同范围内更好,更接近实际使用的位置。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM