It’s crucial for Testers to know different data exchange formats. JSON is one of them. It’s become popular as an exchange format for web services as well as a format for configuration files.

JSON is tighly integrated with JavaScript, its grammar is a subset of JavaScript expressions. Therefore, as a step one in learning more about JSON, I recommend learning a bit about JavaScript. I’ll start exactly like so.

In JS, the following is a statement whose right-hand side is an expression, more precisely an object literal:

let language = {
    name: 'JavaScript'
};

That’s really similar to how we could represent this in JSON:

{
    "name": "JavaScript"
}

However, there are a few key differences that are worth mentioning:

  • object literals don’t need to have quotes around keys, JSON always has to have double quotes around keys and values
  • object literals can use single or double quotes around keys and values, JSON uses only double quotes and they have to be around keys
  • object literals are instances of Object and they are mutable by default, JSON is just a string, so if I want to e.g. add a property to it, I have to parse it first

I’ve also heard some people say “JSON object”. Such a thing doesn’t exist, JSON is just a string, it’s not an object. If you want to make an object out of it, you need to parse it (e.g. using JSON.parse()).

Another key difference between JS object literals and JSON is when we have a look at values.

There’re several values allowed in JSON:

  • string
  • number
  • boolean (e.i. true or false)
  • array
  • object
  • null

That’s close to JS, but JS can also have function, undefined and in terms of numbers, JSON doesn’t allow special values like NaN, +Infinity, -Infinity, no leding zeros are allowed, decimal point has to be preceded and followed by at least one digit, and no hexadecimal numbers in the format 0xff are allowed. To see some of these differences, this is perfectly valid in JS:

let a = {
    a: undefined
};

let b = {
    b: NaN
};

let c = {
    c: Infinity
};

let d = {
    d: 0xff
};

let e = {
    e: .5
};

but invalid in JSON:

{
    "a": undefined
}
{
    "b": NaN
}
{
    "c": Infinity
}
{
    "d": 0xff
}
{
    "e": .5
}

If you want to represent e.g. “not a number” value, one option is to encode it as a string in JSON:

{
    "a": "NaN"
}

In terms of numbers, the following formats are allowed in JSON:

{
    "a": 5,
    "b": 5.0,
    "c": 5e0,
    "d": -5,
    "e": -5.0,
    "f": -5e1,
    "g": 5E0
}

Strings in JSON are, just like keys, enclosed in double quotes:

{
    "a": "something"
}

Some characters in strings have to be escaped: \n, \r, \f, \b, \t, \\, \/, \", and \u for hexadecimal codes like \u005c or \u005C.

Most of the time, you can see JSON strings to be nicely formated. But that doesn’t have to be the case in order to have a valid JSON. There’re six tokens in JSON: {, }, [, ], ,, :, and it’s perfectly ok to put a whitescape (one of these: \t, \r, \n, ` `) before or after them. So the following examples are all valid:

{
    "a": 5
}
{"a": 5}
{         "a":                   5                   }

JSON also supports arrays and objects, let’s see two examples:

{
    "languages": [
        "Java",
        "Javascript"
    ]
}
{
    "vouchers": {
        "bronze": {
            "ids": [1, 2, 3]
        },
        "silver": {
            "ids": [8]
        },
        "gold": {
            "ids": [77]
        }
    }
}
[
    {
        "a": 1
    },
    {
        "b": 2
    }
]

In terms of keys, JSON doesn’t pose any limitations as to what strings can become a key. It only has to be a string, but it doesn’t need to be unique, it can contain whitespaces, there’s no significance assigned to the ordering of key-value pairs.

Possible pitfalls

When working with web services that use JSON as an exchange data format, I’ve seen that sometimes these two JSONs are treated by the development team as if they were the same:

{
    "a": 5
}
{
    "a": "5"
}

What I mean is sending numbers as strings.

While this might be perfectly ok in many languages, or there might be some (automatic) coercion of data types, it might be a problem in JavaScript.

console.log(1 + 5);   // 6
console.log(1 + "5"); // 15

+ operator is used in various ways, and one usage is it converts its operands to strings when at least one operand is of type string. The second line prints 15, not 6 as is most likely wanted. So working with the latter JSON might have unexpected outcomes in situations when we really want to work with numbers.

Working with JSON in JavaScript

If you’re working with JSON strings in JavaScript, you have to know at least JSON.parse() and JSON.stringify() methods.

JSON.stringify()

This function takes an object literal and creates a JSON string from it. The function has three parameters, replacer and space are not mandatory, though.

Just a few examples:

let a = {
    a: 1,
    b: 2
};
JSON.stringify(a); // {"a":1,"b":2}

If you want to format the JSON string, you can change the last argument:

let a = {
    a: 1,
    b: 2
};
JSON.stringify(a, null, 4);

which will result in this string:

{
    "a": 1,
    "b": 2
}

You can also use any character(s) as the last argument:

let a = {
    a: 1,
    b: 2
};
JSON.stringify(a, null, '--');

which will end up being:

{
--"a": 1,
--"b": 2
}

Just beware this is not a valid JSON anymore.

The replacer parameter can change the process of stringification, you could for example decide that some properties should not be included in the result, or that you want to whitelist some properties. That’s why this parameter exists. Let’s see some examples:

let a = {
    a: 1,
    b: 2
};
JSON.stringify(a, ['a'], 2);

This will whitelist properties with a key, so the result will include only such properties:

{
  "a": 1
}

Or you can use a function expression:

let numbersGreaterThan = function (key, value) {
    if (typeof value === 'number') {
        if (value <= 10)
            return undefined;
    }
    return value;
};

var a = {
    a: 1,
    b: 2,
    c: 11
};

JSON.stringify(a, numbersGreaterThan, 2);

which will filter out properties a and b and result in:

{
  "c": 11
}

JSON.parse()

This function takes a string formatted as JSON and creates a JavaScript value from it.

JSON.parse('{ "hello": 123, "world": 456 }');

that will result in a JavaScript value: { hello: 123, world: 456 }.

JSON.parse() can also take a second parameter reviver that could be used for post-processing of the parsed data. An example could be you need to transform a string representing a date to a Date object. Then you use a function passed as the second argument for that.

Further resources

I recommend the MDN site, you can check out JSON.stringify(), JSON.parse(), and JSON pages, they contain a lot of examples you can learn from. This book is a good reference point for ECMAScript 5, many of the things are still valid, so you can quickly look them up (the book is available online for free). For a quick check if a string is a valid JSON, you can go to JSONLint.