In this blog post, we will discuss the JavaScript Object Notation (JSON) data format. The target audience is CCNA and CCNP candidates preparing for the exams.
The content provides fundamental overview of the following topics:
CCNA exam
6.7 Interpret JSON encoded data
CCNP ENCOR exam
6.2 Construct valid JSON encoded file
JSON Overview
JSON is an open standard text-based file format to store and exchange serialized data. Serialization is the process of converting an object into a format that can be stored or transported to later recreate it.
JSON was originally derived from JavaScript, however, many other programming languages can interpret and generate JSON data. Figure 1 shows how JSON components fit together.
JSON text can represent one of the following values (orange and blue circles):
- String
- Number
- Literal name (false, true, null)
- Array
- Object
JSON simple values
Strings, numbers, and literals
The simple values can represent some text or number and cannot contain other values. For example, below are examples of valid JSON texts:
Listing 1
"I'm a JSON" 100 true null
As per RFC 8259, JSON text can be represented by any serialized value. Some specifications of JSON require that valid JSON text must be an object or an array.
Note that the string values must be enclosed in quotation marks.
JSON structured data values
Structural characters
JSON values that represent structured data (blue circles) created using 6 structural characters listed below:
- Square brackets [] – beginning and end of an array
- Curly brackets {} – beginning and end of an object
- Colon : – Name separator
- Comma , – Value separator
JSON allows the use of whitespaces, such as spaces, tabs, and new lines to format the text for readability. Contrasted to Python, indentation is used only for readability.
Array
An array contains zero or multiple ordered elements. Elements don’t have to be of the same type.
Listing 2
[ "abc", 23, null ]
Object
An object contains zero or multiple members, separated by commas. Each member is in the name: value format. Name must be unique within an object.
Listing 3
{ "address": "192.168.8.1", "mask": "255.255.255.255" }
Nested Objects and Arrays
Arrays and objects can contain both simple values, other arrays, and other objects.
For instance, below is the object, as we can see it starts with an opening curly brace. The object contains 2 members with name tags of “primary_address” and “secondary_address”. Each of the member’s value is another object that consists of 2 more members, named “address” and “mask”.
Listing 4
{ "primary_address": { "address": "192.168.8.1", "mask": "255.255.255.255" }, "secondary_address": { "address": "192.168.9.1", "mask": "255.255.255.255" }, }
Let’s create an array that will contain 2 objects representing addresses. The opening square bracket starts the definition of an array. Then we wrap each of the members from the previous example into curly brackets to create an object, as array stores elements – not members consisting of name: value pairs.
Listing 5
[ { "primary_address": { "address": "192.168.8.1", "mask": "255.255.255.255" } }, "secondary_address": { "address": "192.168.9.1", "mask": "255.255.255.255" }, } ]
How to interpret JSON encoded data
In one of the previous blog posts dedicated to REST API, we’ve programmatically extracted a JSON representation of an interface from the IOS-XE router. This listing below shows several router’s interfaces, so we can have some arrays in the example.
Listing 6
{ "Cisco-IOS-XE-native:interface": { "GigabitEthernet": [ { "name": "1", "ip": { "address": { "primary": { "address": "192.168.7.4", "mask": "255.255.255.0" } } }, "mop": { "enabled": false, "sysid": false }, "Cisco-IOS-XE-ethernet:negotiation": { "auto": true } }, { "name": "2", "shutdown": [ null ], "mop": { "enabled": false, "sysid": false }, "Cisco-IOS-XE-ethernet:negotiation": { "auto": true } } ] } }
Let’s interpret this document. Figure 2 shows the structure of the JSON code from the example above.
The top-level object (#1) has a single member with the name of “Cisco-IOS-XE-native:interface”. This member’s value is another object (#2).
The object #2 also has a single member named “GigabitEthernet”, whose value is an array (#3).
Array contains 2 elements – object #4 and object #5.
Object #4 has 4 members, with the following names:
- “name”
- “ip”
- “mop”
- “Cisco-IOS-XE-ethernet:negotiation”
Member called “name” has a string value of “1”. The next member named “ip” has an object (#6) as a value. Object #6 has a single member with the name of “address” having another object (#7) as a value.
The pattern of finding array elements and object members should be apparent by now.
How to construct JSON encoded data
Online Tools
The easiest way to create a JSON encoded data is to use one of the available online JSON editors. For example, one is available via this URL. It automatically checks JSON file syntax, which can be useful to find a missing bracket. The other feature of this tool is the ability to auto-format code into a compact format or full format (with line breaks and indentation, as shown in the previous example).
The screenshot of the tool with JSON text from the previous example is shown below.
Python Collections Overview
To continue with the following examples, we recommend checking this article (URL) for a brief quick start and Python installation instructions.
Let’s discuss several Python fundamental topics before proceeding with the practical examples.
- Data Structures: lists and dictionaries
Lists and dictionaries are examples of collections in Python. Python’s JSON module maps lists to JSON arrays, and dictionaries to JSON objects.
The syntax is identical between matching pairs of data structures, as shown in Figure 4.
The listing below shows an example of a list and a dictionary definition in Python.
A list is defined in Python using square brackets. Python uses None instead of null literal in JSON.
The dictionary is wrapped with curly brackets and has familiar from JSON example syntax. JSON’s name tag (value just before the colon) corresponds to a dictionary key in Python. It is followed by a colon and a value, which is in our example a string.
Listing 7
sample_list = [ "abc", 23, None ] sample_dictionary = { "address": "192.168.8.1", "mask": "255.255.255.255" }
Let start interactive Python prompt to demonstrate how to work with lists and dictionaries.
Listing 8
c:\PythonExamples>python Python 3.8.3 (tags/v3.8.3:6f8c832, May 13 2020, 22:20:19) [MSC v.1925 32 bit (Intel)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> sample_list = [ "abc", 23, None ] >>> sample_dictionary = { "address": "192.168.8.1", "mask": "255.255.255.255" }
Both lists and dictionaries can be passed to print() method, which will display their string representation.
Listing 9
>>> print(sample_list) ['abc', 23, None] >>> print(sample_dictionary) {'address': '192.168.8.1', 'mask': '255.255.255.255'}
We can access individual elements in a list using their index position.
Listing 10
>>> print(sample_list[0]) abc >>> print(sample_list[1]) 23
To extract values for a specific dictionary key, we can use the key’s name as an index.
Listing 11
>>> print(sample_dictionary["address"]) 192.168.8.1 >>> print(sample_dictionary["mask"]) 255.255.255.255
- Working with files
We will save and read JSON files to and from a file saved on the disk in the next examples.
To open a file for read access in Python the following code is used:
Listing 12
with open("json_test.json","r") as json_file: … some code that makes use of json_file
To open the same file for write access, use “w” instead of “r” as a parameter for the open() function. Use of keyword “with” ensures that the file is properly closed after the use.
Decoding JSON in Python example
Python module called json provides JSON encoding and decoding capabilities. There are 2 methods performing these functions:
- dumps – Python data structure to JSON text
- loads – JSON text into Python data structure
Let’s create a text file containing JSON text from Listing 6 and save it as json_ios_xe.json.
As the next step, we will create a file named json_example.py that will have the following Python code in it.
Listing 13
import json with open("json_ios_xe_interfaces.json", "r") as json_file: json_file_content = json_file.read() decoded_json = json.loads(json_file_content) print(decoded_json) print() print(type(decoded_json))
Line #1 imports json module, so we can use its feature in our code.
The code in line #3 opens our file for read-only access. The access to the file content is provided via json_file variable. The code in line #4 reads-in content of the file into a string variable.
Line #5 uses json.loads() function to read the string representation of JSON text. The returned value is assigned to the decoded_json variable. As the JSON text is a JSON object, the decoded_json object will be a Python dictionary.
Line #7 prints the Python dictionary, followed by an empty line created by line #8. Finally, line #9 prints out the type of decoded_json object, so we can validate that it is in fact a Python dictionary.
Let’s run the code and see the result.
Listing 14
c:\PythonExamples>python json_example.py {'Cisco-IOS-XE-native:interface': {'GigabitEthernet': [{'name': '1', 'ip': {'address': {'primary': {'address': '192.168.7.4', 'mask': '255.255.255.0'}}}, 'mop': {'enabled': False, 'sysid': False}, 'Cisco-IOS-XE-ethernet:negotiation': {'auto': True}}, {'name': '2', 'shutdown': [None], 'mop': {'enabled': False, 'sysid': False}, 'Cisco-IOS-XE-ethernet:negotiation': {'auto': True}}]}} <class 'dict'>
Encoding to JSON in Python example
In this example, we will use the dictionary created in the previous example, change the IP address to “192.168.7.5” and will encode it as another JSON file.
The first task is to identify the full path to the IP address. We have several nested layers of hierarchy within the outer-most dictionary. To access inner dictionaries and lists we will append [<index_or_key_name>] to the parent identifier.
Full path to value of ‘address’ key will be:
Listing 15
decoded_json['Cisco-IOS-XE-native:interface']['GigabitEthernet'][0]['ip']['address']['primary']['address']
In the example above the index of [0] is used, as the ‘GigabitEthernet’ key has the value of a list and we are interested in the first element.
Below is the full listing of a program code that changes the IP address and saves it as a new JSON file on the disk.
Listing 16
import json with open("json_ios_xe.json", "r") as json_file: json_file_content = json_file.read() decoded_json = json.loads(json_file_content) decoded_json['Cisco-IOS-XE-native:interface']['GigabitEthernet'][0]['ip']['address']['primary']['address'] = \ "192.168.7.5" encoded_json_compact = json.dumps(decoded_json) encoded_json_indented = json.dumps(decoded_json, indent = 4) with open("json_ios_xe_compact.json", "w") as json_file: json_file.write(encoded_json_compact) with open("json_ios_xe_indented.json", "w") as json_file: json_file.write(encoded_json_indented)
Line #8 sets the value to a new IP address. Lines #11 and #12 create a string containing JSON text, it passes our modified dictionary called decoded_json to json.dumps() function. The example demonstrates that the named parameter called “indent” can be passed to the dumps() method to perform the formatting of the JSON file.
Line #15 and #18 saving the resulted text to files on the disk.
Let’s run the code and see the result.
Listing 17
c:\PythonExamples>python json_example.py
Two new files are created in c:\PythonExamples folder, as shown in the screenshot below.