On this page:
Getting started
Let there be JSON
What’s data without a little self-reference?
Finders, keepers
JSON:   The whole shebang
JSON:   Flattened edition
8.13

Recitation 7: Parametric data and visitors🔗

Goals: Practice implementing the visitor pattern and using generics in a novel class.

Related files:
  tester.jar  

Goals:

Getting started🔗

Copy and paste the following (hopefully) familiar code into IntelliJ:
// Represents functions of signature A -> R, for some argument type A and // result type R interface IFunc<A, R> {
R apply(A input);
}
 
// generic list interface IList<T> {
// map over a list, and produce a new list with a (possibly different) // element type <U> IList<U> map(IFunc<T, U> f);
}
 
// empty generic list class MtList<T> implements IList<T> {
public <U> IList<U> map(IFunc<T, U> f) {
return new MtList<U>();
}
}
 
// non-empty generic list class ConsList<T> implements IList<T> {
T first;
IList<T> rest;
 
ConsList(T first, IList<T> rest) {
this.first = first;
this.rest = rest;
}
 
public <U> IList<U> map(IFunc<T, U> f) {
return new ConsList<U>(f.apply(this.first), this.rest.map(f));
}
}

Let there be JSON🔗

JSON is an industry-standard data structure that allows for the storage and sharing of tree-shaped data. Below is a definition of a subset of JSON (more will come later). A JSON can either be a blank value, a number, boolean, or string. In the real world, JSON numbers can have decimal values, but for our purposes we’ll stick to integers.

Copy and paste the following definition into IntelliJ:
// a json value interface JSON {}
 
// no value class JSONBlank implements JSON {}
 
// a number class JSONNumber implements JSON {
int number;
 
JSONNumber(int number) {
this.number = number;
}
}
 
// a boolean class JSONBool implements JSON {
boolean bool;
 
JSONBool(boolean bool) {
this.bool = bool;
}
}
 
// a string class JSONString implements JSON {
String str;
 
JSONString(String str) {
this.str = str;
}
}

What’s data without a little self-reference?🔗

As the definition for JSON stands in our current form, it’s not very useful. After all, it doesn’t allow for our favorite type of data: lists! Let’s extend the definition to allow for them.

Copy and paste the following definition into IntelliJ:
//a list of JSON values class JSONList implements JSON {
IList<JSON> values;
 
JSONList(IList<JSON> values) {
this.values = values;
}
}

Do a sanity check: does your JSON interface have anything other than an accept method? If so, you have likely not implemented the visitor pattern correctly. Ask a staff member for further clarification. If not, continue onwards.

Finders, keepers🔗
JSON: The whole shebang🔗

As it turns out, there’s even more to JSON than we’ve seen! JSON values can also be a list of pairs, where each pair is composed of a string, often called a key, and an assocated JSON value. Note that in proper JSON data, keys cannot repeat within the same list of pairs; we ignore this for now.

What does this remind you of? What does JSON stand for anyways?

Copy and paste the following definition into IntelliJ:
// a list of JSON pairs class JSONObject implements JSON {
IList<Pair<String, JSON>> pairs;
 
JSONObject(IList<Pair<String, JSON>> pairs) {
this.pairs = pairs;
}
}
 
// generic pairs class Pair<X, Y> {
X x;
Y y;
 
Pair(X x, Y y) {
this.x = x;
this.y = y;
}
}

JSON: Flattened edition🔗

Design a visitor, JSONFlatten, which flattens a JSON to a list of pairs of its atomic elements and the path needed to get there. For example, if we had the following JSON object (written in JSON notation):

{
"bizz": "buzz",
"foo": false,
"abc": [null, {"efg": 42}]
}

we would want to produce an IList<Pair<String, JSON>> with these elements, in this order:

new Pair<String, JSON>("bizz", new JSONString("buzz"));
new Pair<String, JSON>("foo", new JSONBool(false));
new Pair<String, JSON>("abc-0", new JSONBlank());
new Pair<String, JSON>("abc-1-efg", new JSONNumber(42));