'Scala Play Framework: cannot generate object from json with null values

I'm new to Scala and the Play Framework. I have written the following controller:

@Singleton
class MyController @Inject()(val controllerComponents: ControllerComponents) extends BaseController {

  implicit val newMeasurementJson: OFormat[MeasurementModel] = Json.format[MeasurementModel]

  def addMeasurement(): Action[AnyContent] = Action { implicit request =>
    val content = request.body

    val jsonObject: Option[JsValue] = content.asJson
    val measurement: Option[MeasurementModel] =
      jsonObject.flatMap(
        Json.fromJson[MeasurementModel](_).asOpt
      )

    ...
  }
...
}

Where the endpoint receives the following JSON:

{
    "sensor_id": "1029", 
    "sensor_type": "BME280", 
    "location": 503, 
    "lat": 48.12, 
    "lon": 11.488, 
    "timestamp": "2022-04-05T00:34:24", 
    "pressure": 94667.38, 
    "altitude": null, 
    "pressure_sealevel": null, 
    "temperature": 3.91, 
    "humidity": 65.85
}

MeasurementModel looks like this:

case class MeasurementModel(
                        sensor_id: String,
                        sensor_type: String,
                        location: Int,
                        lat: Float,
                        lon: Float,
                        timestamp: String,
                        pressure: Float,
                        altitude: Int,
                        pressure_sealevel: Int,
                        temperature: Float,
                        humidity: Float) {

}

Through testing I have seen that the null values in the JSON are causing the creation of the measurement object to be unsuccessful. How can I successfully handle null values and have them set in the generated MeasurementModel object?



Solution 1:[1]

The datatypes that can store null are Null and Option[]. Consider the following REPL code: `

scala> val mightBeIntOrNull: Option[Int] = Option(1)
val a: Option[Int] = Some(1)
scala> val mightBeIntOrNull: Option[Int] = null
val a: Option[Int] = null

The Option wraps the Int value in Some, which can be extracted by pattern matching.

scala> val mightBeIntOrNull: Option[Int] = Option(1)
val mightBeIntOrNull: Option[Int] = Some(1)

scala> mightBeIntOrNull match {
 | case Some(myIntVal) => println("This is an integer :" + myIntVal)
 | case _ => println("This might be a null")
 | }
This is an integer :1

scala> val mightBeIntOrNull: Option[Int] = null
val mightBeIntOrNull: Option[Int] = null

scala> mightBeIntOrNull match {
 | case Some(myIntVal) => println("This is an integer :" + myIntVal)
 | case _ => println("This might be a null")
 | }
This might be a null

As Gaƫl J mentioned, you should add Option for the desired datatype in your case class

So the solution can be to wrap the datatype in option where you expect a null. Like:

{
"altitude": Option[Float], 
"sensor_type": Option[String], 
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1