0

I'm new to Scala. How do I handle the JsNull value in my code?

I'm using json4s to convert the JSON to a map. Should I somehow be converting JsNull to an Option?

Example:

Play JSON : creating json

val jsonA: JsValue = Json.obj(
      "name" -> "Bob",
      "location" -> "Irvine",
      "resident" -> "No",
      "nick-name" -> "Bigwig",
      "age" -> "6",
      "role" -> JsNull,
      "car" -> "BMW",
      "multiple-residents" -> JsArray(Seq(
        JsObject(Seq(
          "name" -> JsString("Fiver"),
          "age" -> JsNumber(4),
          "role" -> JsObject(Seq(
                      "position" -> JsString("Fiver"),
                      "" -> JsNumber(4),
                      "role" -> JsString("janitor")
                    ))
        ))
      ))
)

json4s : parsing the json

var jsonAMap:Map[String, Any] = Map()
  val jsonAString: String = Json.stringify(jsonA)
  jsonAMap = jsonStrToMap(jsonAString) 

After the JsValue is converted to a Map using json4s it looks like this:

Map(name -> Bob, location -> Irvine, role -> null, resident -> No, car -> BMW, multiple-residents -> List(Map(name -> Fiver, age -> 4, role -> Map(position -> Fiver,  -> 4, role -> janitor))), age -> 6, nick-name -> Bigwig)

When I create a create a List of the values, I end up with a null value in my list. Once I'm pattern matching all the values of the list I end up trying to patter match a null which is not possible (I'm sure I'm not supposed to use a will card for all my cases, but I'm learning) :

for(i <- 0 to beforeValsList.length - 1){
  beforeValsList(i) match { 
    case _ : Map[_,_] => 
      compareJson(
        beforeValsList(i).asInstanceOf[Map[String,Any]], 
        afterValsList(i).asInstanceOf[Map[String,Any]], 
        rdeltaBefore, rdeltaAfter, sameKeyList(i).toString()
      )
    case _ if (beforeValsList(i) != afterValsList(i)) => 
      // if i'm from a recursion, build a new map and add me 
      // to the deltas as a key->value pair
      rdeltaBefore += sameKeyList(i).toString -> beforeValsList(i)
      rdeltaAfter += sameKeyList(i).toString -> afterValsList(i)
    case _ => 
      println("catch all: " + beforeValsList(i).toString 
        + " " + afterValsList(i).toString)
  } 
}

json4s converts JsNull to a null. Should I do a null check:

if(!beforeValsList(i) == null){
      beforeValsList(i) match{...}
}

Or is there a way for me to change the null to an Option when I'm putting the values from the Map to a List?

I'm not sure what best practices are and why jsno4s changes JsNull to null instead of an Option, and whether or not that's possible.

Cheers.

Peter Neyens
  • 9,770
  • 27
  • 33
krzasteka
  • 427
  • 1
  • 4
  • 14
  • Your question is not that clear, maybe you could give us a more [concise example](http://stackoverflow.com/help/mcve) of how you ara having trouble handling `JsNull`. – Peter Neyens Jul 16 '15 at 19:53
  • An easy way to get the difference between two json strings might be to use [json4s `Diff`](https://github.com/json4s/json4s#merging--diffing) – Peter Neyens Jul 16 '15 at 19:55
  • Hi Peter, thanks for commenting, I hope I made my question clearer and concise. I need more than a diff. I need two deltas, both to hold everything except for the keys and values that are the same in the two JSONs I'm comparing. I tired to look for a framework to handle this but I couldn't find anything that would work the way I need it to. I'm also a Junior Programmer so I try to do things to better understand things like JSON and to think through problems instead of using frameworks/functions. Thank you for replying, let me know if I can clarify further. :) – krzasteka Jul 16 '15 at 20:57
  • You could put all the values in an `Option` using something like `values.map(Option.apply)`. – Peter Neyens Jul 16 '15 at 21:23

1 Answers1

1

I'm still not sure how you would like to handle JsNull, null (or None if you use Option), but you function to get the difference between the Map[String, Any] before and after can be simplified :

type JsonMap = Map[String, Any]

def getMapDiffs(mapBefore: JsonMap, mapAfter: JsonMap) : (JsonMap, JsonMap) = {
  val sameKeys = mapBefore.keySet intersect mapAfter.keySet
  val startAcc = (Map.empty[String, Any], Map.empty[String, Any])
  sameKeys.foldLeft(startAcc){ case (acc @ (deltaBefore, deltaAfter), key) =>
    (mapBefore(key), mapAfter(key)) match {
      // two maps -> add map diff recursively to before diff and after diff
      case (beforeMap: Map[_, _], afterMap: Map[_, _]) =>
        val (deltaB, deltaA) = 
          getMapDiffs(beforeMap.asInstanceOf[JsonMap], afterMap.asInstanceOf[JsonMap])
        (deltaBefore + (key -> deltaB), deltaAfter + (key -> deltaA))
      // values before and after are different
      // add values to before diff and after diff
      case (beforeValue, afterValue) if beforeValue != afterValue =>
        (deltaBefore + (key -> beforeValue), deltaAfter + (key -> afterValue))
      // keep existing diff  
      case _ => acc
    }  
  }
}

Which can be used as:

val (mapBefore, mapAfter) = (
  Map("a" -> "alpha", "b" -> "beta", "c" -> "gamma", "d" -> Map("e" -> "epsilon")),
  Map("a" -> "alpha", "b" -> List("beta"), "c" -> null, "d" -> Map("e" -> 3))
)

val (deltaBefore, deltaAfter) = getMapDiffs(mapBefore, mapAfter)
// deltaBefore: JsonMap = Map(b -> beta, c -> gamma, d -> Map(e -> epsilon))
// deltaAfter: JsonMap = Map(b -> List(beta), c -> null, d -> Map(e -> 3))

deltaBefore.toList
// List[(String, Any)] = List((b,beta), (c,gamma), (d,Map(e -> epsilon)))
Peter Neyens
  • 9,770
  • 27
  • 33
  • Peter, wow. How do I learn Scala to be able to think of the problem and solve it the way you just did, I've only started Scala a week and a half ago. I picked up a Scala Cookbook by Alvin Alexander, but I'm not sure of the correct way to learn Scala. Can you point me in the right direction? I'm stunned by how fast you were able to write that. Thank you for the function, I'm going to read my code and try to fully understand your solution. – krzasteka Jul 16 '15 at 23:14
  • @krzasteka Read some Scala books of your choice and try to solve as mutch problems, exercises, ... in a functional / Scala idiomatic manner, but rest assured, it takes most of the people some time to get used to functional programming. Good luck ! – Peter Neyens Jul 16 '15 at 23:20
  • Thanks again! Cheers :) – krzasteka Jul 16 '15 at 23:22