On this page:
Introduction
JSON
Before you go...
6.10

Lab 8 JSON

home work!

Purpose: This lab aims to provide you a practical example of the usefulness of nested recursive data structures.

Textbook References: Chapter 19: The Poetry of S-Expressions, Chapter 20: Incremental Refinement, Chapter 21: Refining Interpreters, Chapter 22: The Commerce of XML

Introduction

This lab relies on the ability for us to parse JSON, and get JSON from the internet. Parsing JSON is not trivial, so we’re providing a teachpack that does this for you. Please save this file (right click > Save as ...) in the same folder as your lab and put (require "lab8-teachpack.rkt") at the top of your file.

JSON

Goals: Practice working with mutually recursive data. Practice working with tree-structured data.

JSON is a lightweight data-interchange format. Originally developed as part of JavaScript, it is now one of the most popular formats for exchanging data on the web. Unlike many other formats, JSON aims to be easy for humans to read, making it nicer to work with. If students are interested in learning more about JSON they can take a look at the official spec here.

; A RawJSON is a String
; This is the string containing actual JSON text as found in the wild
; For example: {"foo":123} is valid JSON so the string "{\"foo\":123}"
; in ISL is a valid RawJSON
 
(define rjson1 "\"Hello World!\"")
(define rjson2 "[{\"name\":\"Nate\",\"email\":\"nathanl@ccs.neu.edu\"}]")

These strings are not very useful without some way to interpret them however. We need to "parse" them into some other data structure in our language that we can deal with.

; A JSON is one of:
; - String
; - Number
; - Boolean
; - 'null
; - JSONArray
; - JSONObject
 
; A JSONArray is a [List-of JSON]
 
; A JSONObject is a (make-object [List-of JSONPair])
(define-struct object [content])
; DO NOT COPY THE ABOVE STRUCTURE DEFINITION INTO YOUR CODE. IT IS ALREADY
; PROVIDED BY THE TEACHPACK.
 
; A JSONPair is a (list Symbol JSON)

Sample Problem Provide some examples of a JSON, a JSONArray, a JSONObject, and a JSONPair. You can give some simple examples, but also provide some more nested examples to show how the data definitions are intertwined

(define JSON1 "hello")
(define JSON2 'null)
(define JSONARRAY1 (list JSON1 JSON2))
(define JSONARRAY2 (list (list JSON1 JSON2) JSON1 (list (list JSON2))))
(define JSONOBJECT1
  (make-object (list (list 'a 1) (list 'b "two") (list 'c 'null))))
(define JSONOBJECT2
  (make-object
      (list
       (list 'numbers (list 1 2 3 4))
       (list 'strings (list "a" "b" "c"))
       (list 'nesting
             (make-object
                 (list (list 'inner1 (list (list 1 2) (list "a" "b")))
                       (list 'inner2 (list (make-object (list (list 'hi "bye"))) "hello"))))))))

Sample Problem Write a template for JSON.

Notes (1) The JSON data representation consists of four data definitions. How many templates do you need?

(2) The data definitions for JSONObjects and JSONArrays both refer to JSON and the latter refer to the former. This must produce mutual recursive templates.

We have provided for you the following functions: json-pair?, json-object?, and json-array?.

; json-temp : JSON -> ???
(define (json-temp ajson)
  (cond
    [(string? ajson) ...]
    [(number? ajson) ...]
    [(boolean? ajson) ...]
    [(symbol? ajson) ...]
    [(json-array? ajson) ...(json-array-temp ajson) ...]
    [(json-object? ajson) ... (json-object-temp ajson) ...]))
 
; json-array-temp : JSONArray -> ???
(define (json-array-temp ja)
  (cond
    [(empty? ja) ...]
    [(cons? ja)
     ... (json-temp (first ja)) ...
     ... (json-array-temp (rest ja)) ...]))
 
 
; json-object-temp : JSONObject -> ???
(define (json-object-temp jo)
  (... (list-of-json-pair-temp (object-content jo)) ...))
 
; list-of-json-pair-temp : [List-of JSONPair] -> ???
(define (list-of-json-pair-temp lojp)
  (cond [(empty? lojp) ...]
        [(cons? lojp) ... (json-pair-temp (first lojp)) ...
         ... (list-of-json-pair-temp (rest lojp)) ...]) ...)
 
; json-pair-temp : JSONPair -> ???
(define (json-pair-temp jp)
  (... (first jp) ... (json-temp (second jp)) ...))

You can get weather data turned into JSON using string->json, one of the functions defined for you by the teachpack you downloaded.

Here’s one that gives us the forecast for today:

(get-url TODAY-FORECAST-URL)

And here’s one that gives us the forecast for the next few days:

(get-url DAILY-FORECAST-URL)

Sample Problem Define weather-data as the result of calling string->json on one of the get-url calls above. This should provide you with real actual JSON to work with which matches our data definition.

(define weather-data (string->json (get-url DAILY-FORECAST-URL)))

Exercise 1 Design a function find-value that takes a JSON and a Symbol and returns the first JSON pair-value where the pair-name is equal to the given symbol. You should produce some dummy value if the pair does not exist. The dummy value returned should not be a JSON. Be sure to test your function thoroughly!

Exercise 2 Design a function max-temp which, given a JSON, returns the maximum temperature from the data as a Number (look at the data you got back from the weather service to see what label they use for the maximum temperature). Beware it might be different based on which URL you use. If there is no temperature given you should produce 0.

Exercise 3 Design a function get-humidity which, given a JSON, returns the humidity from the data as a Number.

Exercise 4 Design a function current-weather which given a JSON returns a [Listof String] of all the "description"s of the current weather from the data for the latest data point.

Exercise 5 Design the function flatten-json that will return a [List-of (list String JSON)], but the second of any element of the list will not be a JSONArray or a JSONObject. The function will work as follows:

  • If a JSON j is a String, Number, Boolean, or 'null, (flatten-json j) will return (list (list X j)), where X is one of "STRING", "NUMBER", "BOOLEAN", or "NULL", depending on what j is.

  • If it is a JSONObject, then the names of its pairs will be string-appended onto the names of the flattening of the associated value.

  • If it is a JSONArray, then the indices of its values will be string-appended onto the names of the flattening of the associated value.

  • When string appending, append "-" between the two string values as to separate one field name from another.

The following check should pass:
(check-expect (flatten-json
               (list 'null '()
                     (make-object
                         (list (list 'greeting
                                     (make-object
                                         (list (list 'formal "hello")
                                               (list 'informal "howdy")
                                               (list 'garbage (list true 1)))))))))
              (list (list "0-NULL" 'null)
                    (list "2-greeting-formal-STRING" "hello")
                    (list "2-greeting-informal-STRING" "howdy")
                    (list "2-greeting-garbage-0-BOOLEAN" true)
                    (list "2-greeting-garbage-1-NUMBER" 1)))

Exercise 6 Redesign flatten-json so that it clearly displays when an empty array is found. For example the check-expect from before would now be:
(check-expect (flatten-json
               (list 'null '()
                     (make-object
                         (list (list 'greeting
                                     (make-object
                                         (list (list 'formal "hello")
                                               (list 'informal "howdy")
                                               (list 'garbage (list true 1)))))))))
              (list (list "0-NULL" 'null)
                    (list "1-EMPTY" '())
                    (list "2-greeting-formal-STRING" "hello")
                    (list "2-greeting-informal-STRING" "howdy")
                    (list "2-greeting-garbage-0-BOOLEAN" true)
                    (list "2-greeting-garbage-1-NUMBER" 1)))

Exercise 7 Design the function weather-graph which given a JSON and a Symbol will return an Image. The image is a bar graph of each value of the given Symbol.

Exercise 8 Challenge problem: Design json=? which determines if two JSONs are equal. Note that two JSONObjects with the same keys (symbols) that map to the same JSON values are equal, even if the order in the list is not the same.

Before you go...

If you had trouble finishing any of the exercises in the lab or homework, or just feel like you’re struggling with any of the class material, please feel free to come to office hours and talk to a TA or tutor for additional assistance.