Skip to main content

JSON syntax validation

· 6 min read
Mattias Kindborg
Developer Offering

Why did we start to validate all JSON code blocks for syntax validity?

The importance of correctness

We’ve all been there, staring at a code example in the docs, only to find out (after a bit of head-scratching and maybe a few muttered words) that it doesn’t actually work. You start tweaking things, guessing what’s wrong, and before you know it you’re deep in what we might call frustration-driven development. Sure, it’s a learning experience, but it’s not the kind we want you to have.

At Axis, we know how valuable your time (and sanity) is. That’s why we’ve introduced automatic JSON syntax validation for all code blocks in our documentation. Our goal? To make sure every JSON example you see is valid, so you can focus on building great things, not debugging copy-paste errors.

For transparency, we’re currently only checking that the JSON examples are syntactically correct. This means you won’t run into missing commas, brackets or quotes, but it’s still possible for an example to have the wrong key or value. Validating the semantics of JSON examples is a much bigger challenge, and while we hope to get there eventually with the help of JSON schemas and OpenAPI specifications, for now we’re happy to help keep your blood pressure in check by catching those pesky syntax errors.

JSON is meant to be easy to read and write, but let’s be honest: it’s also easy to make small mistakes. And when you’re reading docs, your brain tends to fill in the gaps, making it even harder to spot those little issues.

To show just how sneaky these errors can be, let’s play a quick game of Spot the error. Below are three real JSON examples (all of which now have been fixed in our docs, alongside another 150+ fixes). See if you can find the syntax mistake in each one. Expand the solution to check your answer!

Spot the error

Example 1

{
"apiVersion": "1.0",
"data": {
"propertyList": {
"Brand": "AXIS",
"ProdNbr": "Q3505 Mk II",
"Version": "8.20.1",
}
}
}
Solution for example 1

A common mistake is adding a trailing comma after the last property in an object. While this is perfectly fine in JavaScript or TypeScript (and often encouraged by linters for cleaner diffs), JSON does not allow it.

{
"apiVersion": "1.0",
"data": {
"propertyList": {
"Brand": "AXIS",
"ProdNbr": "Q3505 Mk II",
"Version": "8.20.1",
}
}
}

Example 2

{
"apiVersion": "1.0",
"method": "set"
"params": {
"<CustomHeaderName>": "<CustomHeaderValue>"
}
}
Solution for example 2

A subtle but common mistake in JSON is forgetting to add a comma between properties. In the example there’s no comma between the "method": "set" and "params": { ... } lines.

{
"apiVersion": "1.0",
"method": "set"
"params": {
"<CustomHeaderName>": "<CustomHeaderValue>"
}
}

Example 3

{
"apiVersion": "1.0",
"context": "abc",
"method": "getNetworkInfo",
"data": {
"system": {
"tcpEcnMode": "acceptAndInitiate",
"deviceSwitching": {
"mode": "auto",
"devices": [
"eth0",
"eth1"
],
"manualActiveDevices": [
"eth0"
],
"activeDevices": [
"eth0"
]
},
"hostname": {
"useDhcpHostname": true,
"hostname": "somehostname",
"staticHostname": "somestatichostname"
},
"resolver": {
"useDhcpResolverInfo": true,
"nameServers": [
"192.168.0.1",
"fd::1"
],
"staticNameServers": [
"192.168.0.8",
"192.168.0.4"
],
"maxSupportedStaticNameServers": 3,
"searchDomains": [
"example.com"
],
"staticSearchDomains": [
"something.net",
"something-else.org"
],
"maxSupportedStaticSearchDomains": 6,
"domainName": "axis.com",
"staticDomainName:" "abczxcqwe.se"
}
},
"devices": [
{
"name": "eth0",
"type": "wired",
"macAddress": "ac:cc:8e:68:8e:c4",
"partOfBridge": "",
"link": true,
"IPv4": {
"enabled": true,
"configurationMode": "dhcp",
"linkLocalMode": "off",
"addresses": [
{
"address": "169.168.0.165",
"prefixLength": 24,
"origin": "dchp",
"scope": "global",
"broadcast": "192.168.0.255"
},
{
"address": "169.254.211.16",
"prefixLength": 16,
"origin": "linkLocal",
"scope": "link"
}
],
"maxSupportedStaticAddressConfigurations": 1,
"staticAddressConfigurations": [
{
"address": "192.168.0.90",
"prefixLength": 24,
"broadcast": "192.168.0.255"
}
],
"defaultRouter": "192.168.0.1",
"staticDefaultRouter": "192.168.0.1"
},
"IPv6": {
"enabled": true,
"configurationMode": "dhcp",
"addresses": [
{
"address": "fe80::240:8cff:felb:eef5",
"prefixLength": 64,
"origin": "linkLocal",
"scope": "link"
},
{
"address": "fd1c:360:4e4d:a5b2::e42",
"prefixLength": 128,
"origin": "dhcp",
"scope": "site"
}
]
},
"wired": {
"linkMode": "auto",
"8021X": {
"enabled": false,
"status": "authorized",
"mode": "WPA-Enterprise-EAPTLS",
"configurations": [
{
"mode": "WPA-Enterprise-EAPTLS",
"params": {
"identity": "Lobby",
"eapolVersion": "EAPoLv2"
}
}
],
"supportedModes": [
"WPA-Enterprise-EAPTLS"
]
}
}
},
{
"name": "eth1",
"type": "wlan",
"macAddress": "ac:cc:8e:68:8e:c4",
"partOfBridge": "",
"link": false,
"wlan": {
"station": {
"activeSsid": "lobby",
"8021X": {
"enabled": true,
"staus": "Stopped",
"mode": "none",
"configurations": [
{
"mode": "WPA-Personal-PSK",
"params": {
"is_psk_set": false
}
},
{
"mode": "WPA-Personal-HEX",
"params": {
"is_hex_set": false
}
},
{
"mode": "WPA-Enterprise-PEAP-MSCHAPv2",
"params": {
"identity": "",
"is_password_set": false,
"eapolVersion": "EAPoLv1",
"peapVersion": 1,
"label": 1
}
},
{
"mode": "WPA-Enterprise-EAPTLS",
"params": {
"identity": "",
"eapolVersion": "EAPoLv1"
}
}
],
"supportedModes": [
"none",
"WPA-Personal-PSK",
"WPA-Personal-HEX",
"WPA-Enterprise-PEAP-MSCHAPv2",
"WPA-Enterprise_EAPTLS"
]
}
},
"accessPoint": {
"ssid": "hazelnut",
"enabled": false,
"authenticationMode": "WPA-Personal-PSK",
"installationModeSupported": true
}
},
"IPv4": {
"enabled": true,
"configurationMode": "dhcp",
"linkLocalMode": "off",
"addresses": [],
"maxSupportedStaticAddressConfigurations": 1,
"staticAddressConfigurations": [
{
"address": "192.168.0.90",
"prefixLength": 24,
"broadcast": "192.168.0.225"
}
],
"defaultRouter": "",
"staticDefaultRouter": "dhcp"
}
}
]
}
}
Solution for example 3

This mistake would be easy to spot in a small example, but in a large block of JSON like this, it’s much harder to catch. In this case, the error is that the staticDomainName property has the colon on the wrong side of the quotation mark. When scanning through a long list of properties, it’s easy for your eyes to skip right over a small typo like this, another reason why automated validation is so helpful!

{
"apiVersion": "1.0",
"context": "abc",
"method": "getNetworkInfo",
"data": {
"system": {
"tcpEcnMode": "acceptAndInitiate",
"deviceSwitching": {
"mode": "auto",
"devices": [
"eth0",
"eth1"
],
"manualActiveDevices": [
"eth0"
],
"activeDevices": [
"eth0"
]
},
"hostname": {
"useDhcpHostname": true,
"hostname": "somehostname",
"staticHostname": "somestatichostname"
},
"resolver": {
"useDhcpResolverInfo": true,
"nameServers": [
"192.168.0.1",
"fd::1"
],
"staticNameServers": [
"192.168.0.8",
"192.168.0.4"
],
"maxSupportedStaticNameServers": 3,
"searchDomains": [
"example.com"
],
"staticSearchDomains": [
"something.net",
"something-else.org"
],
"maxSupportedStaticSearchDomains": 6,
"domainName": "axis.com",
"staticDomainName:" "abczxcqwe.se"
}
},
"devices": [
{
"name": "eth0",
"type": "wired",
"macAddress": "ac:cc:8e:68:8e:c4",
"partOfBridge": "",
"link": true,
"IPv4": {
"enabled": true,
"configurationMode": "dhcp",
"linkLocalMode": "off",
"addresses": [
{
"address": "169.168.0.165",
"prefixLength": 24,
"origin": "dchp",
"scope": "global",
"broadcast": "192.168.0.255"
},
{
"address": "169.254.211.16",
"prefixLength": 16,
"origin": "linkLocal",
"scope": "link"
}
],
"maxSupportedStaticAddressConfigurations": 1,
"staticAddressConfigurations": [
{
"address": "192.168.0.90",
"prefixLength": 24,
"broadcast": "192.168.0.255"
}
],
"defaultRouter": "192.168.0.1",
"staticDefaultRouter": "192.168.0.1"
},
"IPv6": {
"enabled": true,
"configurationMode": "dhcp",
"addresses": [
{
"address": "fe80::240:8cff:felb:eef5",
"prefixLength": 64,
"origin": "linkLocal",
"scope": "link"
},
{
"address": "fd1c:360:4e4d:a5b2::e42",
"prefixLength": 128,
"origin": "dhcp",
"scope": "site"
}
]
},
"wired": {
"linkMode": "auto",
"8021X": {
"enabled": false,
"status": "authorized",
"mode": "WPA-Enterprise-EAPTLS",
"configurations": [
{
"mode": "WPA-Enterprise-EAPTLS",
"params": {
"identity": "Lobby",
"eapolVersion": "EAPoLv2"
}
}
],
"supportedModes": [
"WPA-Enterprise-EAPTLS"
]
}
}
},
{
"name": "eth1",
"type": "wlan",
"macAddress": "ac:cc:8e:68:8e:c4",
"partOfBridge": "",
"link": false,
"wlan": {
"station": {
"activeSsid": "lobby",
"8021X": {
"enabled": true,
"staus": "Stopped",
"mode": "none",
"configurations": [
{
"mode": "WPA-Personal-PSK",
"params": {
"is_psk_set": false
}
},
{
"mode": "WPA-Personal-HEX",
"params": {
"is_hex_set": false
}
},
{
"mode": "WPA-Enterprise-PEAP-MSCHAPv2",
"params": {
"identity": "",
"is_password_set": false,
"eapolVersion": "EAPoLv1",
"peapVersion": 1,
"label": 1
}
},
{
"mode": "WPA-Enterprise-EAPTLS",
"params": {
"identity": "",
"eapolVersion": "EAPoLv1"
}
}
],
"supportedModes": [
"none",
"WPA-Personal-PSK",
"WPA-Personal-HEX",
"WPA-Enterprise-PEAP-MSCHAPv2",
"WPA-Enterprise_EAPTLS"
]
}
},
"accessPoint": {
"ssid": "hazelnut",
"enabled": false,
"authenticationMode": "WPA-Personal-PSK",
"installationModeSupported": true
}
},
"IPv4": {
"enabled": true,
"configurationMode": "dhcp",
"linkLocalMode": "off",
"addresses": [],
"maxSupportedStaticAddressConfigurations": 1,
"staticAddressConfigurations": [
{
"address": "192.168.0.90",
"prefixLength": 24,
"broadcast": "192.168.0.225"
}
],
"defaultRouter": "",
"staticDefaultRouter": "dhcp"
}
}
]
}
}