Assigned: 2020-09-01
Due Date: 2020-09-08 by 11:59PM U.S. Central Time
Get your copy of the assignment here: https://classroom.github.com/a/Susn6CdI
You may encounter "Non-managed pom.xml file found". You should select "Add as Maven Project"
The goal of this assignment is to get you comfortable with JSON in preparation for the next assignment, in which you will be utilizing JSON to as one component of a more complex project. This week, you’ll get to choose what JSON dataset to work with, and you’ll have some freedom over what you do with that dataset.
Out of the following 4 tasks, you must complete 2 (any additional tasks completed will be counted as extra credit):
Remember that you still have to write tests to ensure that all of the functionality you’ve implemented works correctly.
There is no autograder for this assignment, but you still must submit your GitHub repository to Gradescope.
Serializing (or marshalling) is the process of converting objects in code into a different representation for storage, whether that's JSON, YAML, binary data, or something else. Deserializing (or unmarshalling) is the reverse: converting serialized data into actual objects in your code. In this case, you are transmitting serialized data about a topic of your choosing, so that you can deserialize it into Java objects for processing.
A schema is a definition of how to represent some data. For example, if we want to describe a course at UIUC in words we might say the following:
There is a natural translation between a schema describing objects stored in JSON and objects in Java.
We require that your data is stored as JSON, a data format popularized by the JavaScript programming language. Here's an example of some JSON, representing a group of courses at UIUC:
{
"courses": [
{
"department": "Computer Science",
"number": 126,
"isDifficult": false,
"isTimeConsuming": true,
"professors": ["Craig Zilles", "Carl Evans", "Michael Woodley"]
},
{
"department": "Computer Science",
"number": 421,
"isDifficult": true,
"isTimeConsuming": false,
"professors": ["Elsa Gunter", "Mattox Beckman"]
}
]
}
Things to note about syntactically valid JSON:
{
and }
If you want to check that your JSON is valid, you can use a JSON validator like this one.
How you parse the JSON is up to you. The office hours staff and your code mod will be familiar with the following libraries, so it is recommended that you choose one of them. However, if you prefer a different method, feel free to implement it that way.
If we have some JSON like above, we need to define some Java classes that match the schema so that GSON can deserialize the JSON into instances of those objects.
First, we define the outermost class with its fields:
public class UiucCourses {
private List<Course> courses;
// -- Getter methods omitted --
}
Then we define the inner class, with its own fields (Note that this class should have its own file):
public class Course {
private String department;
private int number;
private boolean isDifficult;
private boolean isTimeConsuming;
private String[] professors;
// -- Getter methods omitted --
}
Finally, we deserialize our JSON into an instance of UiucCourses
:
String myJson = "....."; // Read in JSON from somewhere...
Gson gson = new Gson(); // Create an instance of a GSON parser
UiucCourses courses = gson.fromJson(myJson, UiucCourses.class);
Note that you can define your collections as either Object[]
or List<Object>
at your preference, and GSON will convert it to the appropriate format.
If you need more documentation and examples on how to use GSON, see the User's Guide.
You can also use Jackson to deserialize your JSON. Jackson is already included in your project; all you have to do is import
and use it.
If we have some JSON like above, we need to define some Java classes that match the schema so that Jackson can deserialize the JSON into instances of those objects.
First, we define the outermost class with its fields:
public class CourseExplorer {
private List<Course> courses;
// -- Constructor and getter/setter methods omitted for brevity --
}
Then we define the inner class, with its own fields (Note that this class should have its own file) :
public class Course {
private String department;
private int number;
private boolean isDifficult;
private boolean isTimeConsuming;
private String[] professors;
// -- Constructor and getter/setter methods omitted for brevity --
}
Finally, we deserialize our JSON into an instance of CourseExplorer
:
courses.json
):
File file = new File("src/main/resources/courses.json");
CourseExplorer explorer = new ObjectMapper().readValue(file, CourseExplorer.class);
String myJson = "...";
CourseExplorer explorer = new ObjectMapper().readValue(myJson, CourseExplorer.class);
Note that you can define your collections as either Object[]
or List<Object>
at your preference, and Jackson will convert it to the appropriate format.
If you need more documentation and examples on how to use Jackson, see the User's Guide.
@Before
As with the previous assignment, we expect you to write unit tests as you develop. We're going to introduce a new feature of JUnit to make writing some kinds of tests easier for you: the @Before
annotation.
Here's the motivation: your unit tests should be self contained so that running one test doesn't influence the results of another. When you have objects that are shared between tests, that can be hard to guarantee. When you apply the @Before
annotation to a function in your testing class, it tells JUnit that it should run that function once before each and every one of your unit tests, so we can use an @Before
-annotated method to reset any necessary state between test cases.
Specifically, this is great for tests involving GSON:
public class UiucCoursesTest {
private Gson gson;
@Before
public void setUp() {
gson = new Gson();
}
@Test
public void testCaseOne() {
gson.fromJson(/* ... */);
}
@Test
public void testCaseTwo() {
gson.fromJson(/* ... */);
}
}
Now we don't have to repeat the line Gson gson = new Gson()
in all of our tests, and the GSON instance will be reset before each and every one of our unit tests. For more information, see JUnit's documentation.
Here’s some sample JSON: https://github.com/jdorfman/awesome-json-datasets, and here’s some API’s that may return JSON: https://github.com/public-apis/public-apis. Feel free to find your own JSON as well! Just make sure there’s enough data to fulfill the requirements.
You may download a copy of this data to use in your development process and for testing, but you should avoid copy-pasting the JSON into your code.
In order to do anything meaningful with the data in Java, you have to convert it into Java objects.
Specifically you should:
Once you have your data as Java objects, you need to be able to filter and process it to get more interesting results.
If you choose to do the filtering functions, you'll need at least four different functions that filter collections of information based on some criteria. Some examples (from some soccer JSON):
For the analysis option, you'll need at least four different functions that do some analysis and produce a computed result. Some examples (also from the soccer JSON):
For the purposes of this assignment, a filtering function takes in a collection and produces another collection with the same number or fewer elements. An analysis function gives you some kind of result, whether that result is a list or a number or a boolean.
Your functions should not mutate any of the data they operate on; they should return new collections/numbers/objects as necessary.
Options 3 & 4 are for those who are already comfortable with JSON. If you’re unsure if your idea qualifies, don’t hesitate to ask your moderator or check on Campuswire.
This assignment is deliberately open-ended with regards to how you choose to implement the requirements; design decisions like what kinds of classes and functions to write are left up to you. Be creative! We have avoided giving you any starter code to encourage you to reason about how to organize your project.
As always, testing is essential. However, because you’re deciding the functionality, it is up to you to make sure that you’re thoroughly testing it.
Note -- You may have to mark the test directory as “Test Sources Root”. This can be done by right clicking on the directory and selecting “mark directory as”.
As always, try to use the best code design and coding style. Continue to focus on writing good names (you have a lot of freedom in naming for this assignment) and think about how to cleanly lay out your code so that it's easy for an outsider (your moderator) to follow. You might find Chapter 4 of the textbook and Section 4 of the Google Java Style Guide to be good resources for this.
As you work on this project, you should commit small pieces of functionality as you write them. Don't make a single mega-commit at the very end! We expect your commit history to demonstrate you following some sort of iterative design process. For example: writing some tests, writing some code, changing some tests, fixing a test, writing more code, etc. Commits are cheap. Make lots of them!
Specific things we will be checking for:
Note that this rubric is not a comprehensive checklist; it’d be impossible to list out every single thing that you should be considering while writing your code.
Your grades for each section of the rubric will be weighted as follows: