Extract Options from potentially null JSON values using for expression

后端 未结 4 1755
日久生厌
日久生厌 2021-01-19 02:09

I have a JSON document where some values can be null. Using for expressions in json4s, how I can yield None, instead of nothing?

The following will fail to yield whe

相关标签:
4条回答
  • 2021-01-19 02:36

    According to the documentation,

    Any value can be optional. Field and value is completely removed when it doesn't have a value.

    scala> val json = ("name" -> "joe") ~ ("age" -> (None: Option[Int]))

    scala> compact(render(json))

    res4: String = {"name":"joe"}

    Explaining why your for comprehension doesn't yield anything.
    Of course, a null value is mapped to None internally.

    0 讨论(0)
  • 2021-01-19 02:44
    scala> object OptExtractors {
         |
         |   // Define a custom extractor for using in for-comprehension.
         |   // It returns Some[Option[Double]], instead of Option[Double].
         |   object JDoubleOpt {
         |     def unapply(e: Any) = e match {
         |       case d: JDouble => Some(JDouble.unapply(d))
         |       case _ => Some(None)
         |     }
         |   }
         | }
    defined object OptExtractors
    
    scala>
    
    scala> val j = parse("""{
         |   "FormattedID" : "the id",
         |   "PlanEstimate" : null
         | }""")
    j: org.json4s.JValue = JObject(List((FormattedID,JString(the id)), (PlanEstimate,JNull)))
    
    scala>
    
    scala> import OptExtractors._
    import OptExtractors._
    
    scala>
    
    scala> for {
         |   JObject(list) <- j
         |   JField("FormattedID", JString(id)) <- list
         |   JField("PlanEstimate", JDoubleOpt(points)) <- list
         | } yield (id, points)
    res1: List[(String, Option[Double])] = List((the id,None))
    
    0 讨论(0)
  • 2021-01-19 02:56

    The last command should look like:

    for {
      JObject(thing) <- res1
    } yield thing.collectFirst{case JField("PlanEstimate", JDouble(points)) => points}
    

    Or like

    for {
      JObject(thing) <- res1
      points = thing.collectFirst{case JField("PlanEstimate", JDouble(p)) => p}
    } yield points
    
    0 讨论(0)
  • 2021-01-19 02:57

    What about this

     for {
          JObject(thing) <- res1      
          x = thing.find(_._1 == "PlanEstimate").flatMap(_._2.toOption.map(_.values))
         } yield x
    
    0 讨论(0)
提交回复
热议问题