2023-07-22

The Developer's Guide To JSON

Everything you need to know to get started with JSON - examples, web requests, serialization and more.


Example JSON

JSON (JavaScript Object Notation) is a widely used data-interchange format that can represent hierarchical, structured data in a way that's easy to read for humans and computers alike. Though based upon the syntax of JavaScript objects, JSON is a text format making it language agnostic and therefore a favourite of developers everywhere.

How is JSON structured?

JSON relies on two types of data structure: a collection of name-value pairs, referred to as an Object in JSON but in most programming languages a dictionary, hash map, hash table, record, etc; and an ordered list of values, referred to as an array in JSON but also vectors, lists, and sequences in other languages. These structures can then be combined with some types to store just about anything you want inside a JSON.

JSON Types

The JSON format supports six different types of data. The first two we've already seen - Objects and Arrays. The next four are common types seen throughout programming languages.

JSON Examples

Below is a small example demonstrating all of the available JSON types.

{
  "null": null,
  "boolean": true,
  "number_int": 1,
  "number_float": 1.0,
  "string": "string",
  "array": [
    1,
    2,
    3,
    true,
    "string",
    {
      "another_object": 2.0
    }
  ],
  "object": {
    "some_nested_object": true
  }
}

As you can see, a JSON object can store another JSON object within itself. This gives you the ability to infinitely nest your data and theoretically store any complex structure you wish.

Let's move on to a more practical example. I'd like to store some information about my Zoo. I'd also like to keep track of the individual animals as well as the enclosure they're in. I could use a JSON to store this information like so.

{
  "name": "Andrew's Zoo",
  "address": "65 Zoo Lane",
  "openAtWeekends": true,
  "openHoursPerDay": 10,
  "animals": [
    {
      "giraffes": {
        "enclosure": "south",
        "names": [
          "Marty",
          "Melvin"
        ]
      }
    },
    {
      "meerkats": {
        "enclosure": "north",
        "names": [
          "Sergei",
          "Oleg",
          "Bogdan"
        ]
      }
    }
  ]
}

If I went on to open another zoo, I could easily extend the format by making an array containing multiple of these JSON objects, each with a different "name" field. The above JSON has been formatted using an online tool I made, but that doesn't have to be the case. A JSON can be compressed or minified in the following way and also be valid.

{"name":"Andrew's Zoo","address":"65 Zoo Lane","openAtWeekends":true,"openHoursPerDay":10,"animals":[{"giraffes":{"enclosure":"south","names":["Marty","Melvin"]}},{"meerkats":{"enclosure":"north","names":["Sergei","Oleg","Bogdan"]}}]}

Stringify, Parse, Serialize, and Deserialize

Let's say I'd like to send the information about my Zoo over the internet. I'd first agree with the person on the other end that I'll be sending the data in JSON format. Using a POST request as an example, you can specify your "Content-Type" as "application/json" in the header of your request. Then, you can include the JSON data in the body of your request. Depending on the language you're using this may be done automatically! For example, let's take a look at doing this in Python.

import requests

example_url = "https://<some_example_url_here>/post_data"
example_json = {"example": "json"}

response = requests.post(example_url, json = example_json)

There's also something else going on here. Alongside setting the headers, the requests module converts a Python dictionary to a JSON string which can then be sent on as bytes. This raises an interesting question. What happens if the response is a JSON too? You'd receive it as a string which isn't very useful when you want to extract the data within. This is where Stringifying, Parsing, Serializing, and Deserializing come in. Most languages will provide you with a set of functions, possibly within their standard library, that let you convert a JSON string or bytes to an object or structure (parse/deserialize) and then back again (stringify/serialize). Sticking with Python, you'd be able to convert the JSON string response to a dictionary and access certain data like so.

import json

response_dictionary = json.loads(response.text)

specific_data = response_dictionary["example"]

The same thing can be done very easily in other interpreted, weakly-typed languages like JavaScript where the JSON string is just converted to a generic object. But what happens if you're using a strongly typed language such as C++ or Rust? These languages need to know the format of their classes/structs at compile time and their arrays/vectors can only hold one type of data at a time. When it comes to JSON they heavily rely on enumerations which are often provided ready-made by an external library. Let's take a look at how Rust handles this within the serde_json library.

pub enum Value {
    Null,
    Bool(bool),
    Number(Number),
    String(String),
    Array(Vec<Value>),
    Object(Map<String, Value>),
}

The enumeration "Value" can be either one of the six JSON types we defined earlier and can also contain itself, just like the JSON object can. This means in a strongly typed language we can still handle a completely generic JSON, we just have to check which type each field is when we want to use the data. Better still, if we know our incoming JSON won't be changing much from a defined format, we can deserialize it directly into a struct where we won't have to check the type every time we wish to use the data.

use serde::{Deserialize, Serialize};
use serde_json::Result;

#[derive(Serialize, Deserialize)]
struct Zoo{
    name: String,
    address: String,
}

let data = r#"
    {
        "name": "Andrew's Zoo",
        "address": "65 Zoo Lane"
    }"#

let zoo_struct: Zoo = serde_json::from_str(data).unwrap();

This was just one very basic example, but often these libraries can be configured to read JSONs into structs which have optional fields, fields with different names, or even map multiple nested JSONs to nested structs.

Configuration files

Alongside data transmission, JSON is often used for settings and configuration files. For example, if you wanted to publish a JavaScript package to npm, you include a "package.json" file which includes relevant information about the authors, version, license etc. JSON configs can also be used in many web server frameworks like Node.js or Flask to specify the port, host, and any other environment variables you may have.

Useful libraries

Here's a quick summary of some useful libraries which let you handle JSONs in some popular languages.

I've omitted some languages where the standard library has great JSON support. If you'd like to see a longer list, check out the JSON homepage or this excellent compilation on GitHub.

Useful Tools

Sometimes it's just more convenient to visit your web browser to do a small task, like validating a JSON, so I've included some of my own tools down below.

Thanks for reading, you can also find this post on Medium. Next up I'll be diving into the YAML and TOML formats!



Software
JSON