JSON
An Application of Polymorphism

JSON - Reminder

  • JSON is [mostly] used to communicate betweent programming languages

  • Consist of 6 types

    • String
    • Number
    • Boolean
    • Array
    • Object
    • Null
  • In Python

    • json.dumps to convert from Python types to JSON string
    • json.loads to convert from JSON string to Python types
  • In JavaScript

    • JSON.stringify to convert from JavaScript types to JSON string
    • JSON.parse to convert from JSON string to JavaScript types

JSON

  • What about Scala?
    {“timestamp”:1550774961,”message”:”success”,”iss_position”:{“latitude”:”-36.5017”,”longitude”:”-2.8015}}
  • This is valid JSON
  • What Scala type do we use to store this data?
    • Map[String, String]?
    • Map[String, Long]?
    • Map[String, Map[String, String]]?
    • Map[String, Any]?? <- This is the only one that can work, but it’s very restrictive since we can only use the Any methods.
  • We can’t mix types in our Scala data structures
  • .. at least, not without polymorphism

JSON - Library

  • We’ll install a library to help us work with JSON in Scala
    • The Play JSON library
  • Library defines these Scala types
    • JsString
    • JsNumber
    • JsBoolean
    • JsArray
    • JsObject
    • JsNull
  • All these types extend JsValue
  • Map[String, JsValue]
  • The library parses JSON strings and converts all values into one of the Js_types
  • Convert value to their Scala types as needed

Reading JSON

{“timestamp”:1550774961,”message”:”success”,”iss_position”:{“latitude”:”-36.5017”,”longitude”:”-2.8015}}

1
2
3
4
5
6
7
8
9
import play.api.libs.{JsValue, Json}
...
val parsed: JsValue = Json.parse(response)

// unused values, but this is how we would extract message and timestamp
val message: String = {parsed \ "message"}.as[String]
val timestamp: Long = {parsed \ "timestamp"}.as[Long]

val issLocation: Map[String, String] = (parsed \ "iss_position").as[Map[String, String]]
  1. Use the library to extract specific values
  2. Call Json.parse
  3. Parse the JSON string and converts it to a JsValue
  4. Extract values at specific keys
  5. Use \ to get the value at a key as a JsValue
  6. Use as[type] to convert the value to the type you expect, cannot use your custom types without defining how to parse your type

Writing JSON

{“timestamp”:1550774961,”message”:”success”,”iss_position”:{“latitude”:”-36.5017”,”longitude”:”-2.8015}}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def createJSON(message: String, timestamp: Long, location: Location): String = {
val jsonTimestamp: JsValue = Json.toJson(timestamp)
val jsonMessage: JsValue = Json.toJson(message)

val locationMap: Map[String, String] = Map(
"latitude" -> location.latitude.toString,
"longitude" -> location.longitude.toString
)

val jsonMap: Map[String, JsValue] = Map(
"timestamp" -> jsonTimestamp,
"message" -> jsonMessage,
"iss_position" -> jsonLocation
)

Json.stringify(Json.toJson(jsonMap))
}
  • Convert Scala types to JsValue with Json.toJson
    • Cannot use your custom types without defying how to convert your type
  • Call Json.stringify to convert a type to a JSON string
    • Can be any types known to the library(Most of the common Scala types)

Maven

  • We’re using a new library
  • Must download it before use
  • Add it to our Maven file
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    <project xmlns="http://maven.apache.org/POM/4.0.0">
    <gruopId>com.mycompany.app</groupId>
    <artifactId>test1</artifactId>
    <modelVersion>4.0.0</modelVersion>
    <url>http://maven.apache.org</url>
    <version>0.0.1</versiom>

    <dependencies>

    <!-- https://mvnrepository.com/artifact/org.scalatest/scalatest -->
    <dependency>
    <groupId>org.scalatest</groupId>
    <artifactId>scalatest_2.12</artifactId>
    <version>3.0.5</version>
    </dependency>

    </dependencies>

    </project>
  • This is our current Maven file that we used to download scalatest
  • We can add more dependancies to this file
    • Open the Maven sidebal, refresh, then download the new libraries
  • Find new libraries at https://mvnrepository.com
    • An enrmous wealth of shared libraries
    • Search for the new liraries, paste the dependency into you pom.xml file

Lecture Question

Question: In a package named “oop.json” create and complete the “Store” class which is stared below

asJSON returns a JSON string representing an object with keys “cashInRegister” and “inventory” mapping to the values from the two state variables with the same names

fromJSON takes a JSON string in the same format returned from asJSON and sets the state variables to the values from the JSON string

1
2
3
4
5
6
7
8
9
10
11
12
package opp.json

class Store(var cashInRegister: Double, var inventory: List[String]) {

def asJSON(): String = {
""
}

def fromJSON(jsonString: String): Unit = {

}
}

Testing:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package tests

import org.scalatest.FunSuite
import oop.json.Store

class TestSubmission extends FunSuite {

val EPSILON: Double = 0.000001

def equalDoubles(d1: Double, d2: Double): Boolean = {
(d1 - d2).abs < EPSILON
}

test("test the store JSON") {
val store: Store = new Store(550.21, List("eggs", "milk", "waffles"))
val storeJSON: String = store.asJSON()

val store2: Store = new Store(0.0, List())
store2.fromJSON(storeJSON)

assert(equalDoubles(store2.cashInRegister, 550.21))
val actualList: List[String] = store2.inventory.sorted
val expectedList: List[String] = List("eggs", "milk", "waffles").sorted

assert(actualList == expectedList)
}
}