Working with JSON data in iRules - Part 1
When TMOS version 21 dropped a few months ago, I released a three part article series focused on managing MCP in iRules. MCP is JSON-RPC2.0 based, so this was a great use case for the new JSON commands. But it's not the only use case. JSON has been the default data format for the web transport for well over a decade. And until v21, doing anything with JSON in iRules was not for the faint of heart as the Tcl version iRules uses has no native parsing capability. In this article, i'll do a JSON overview, introduce the test scripts to pass simple JSON payloads back and forth, and get the BIG-IP configured to manage this traffic. In part two, we'll dig into the iRules.
JSON Structure & Terminology
Let's start with some example JSON, then we'll break it down.
{
"my_string": "Hello World",
"my_number": 42,
"my_boolean": true,
"my_null": null,
"my_array": [1, 2, 3],
"my_object": {
"nested_string": "I'm nested",
"nested_array": ["a", "b", "c"]
}
}
JSON is pretty simple. The example shown there is a JSON object.
- Object delimeters are the curly brackets you see on lines 1 and 11, but also in the nested object in lines 7 and 10.
- Every key in JSON must be a string enclosed in double quotes. The keys are the left side of the colon on lines 2-9.
- The colon is the separator between the key and its value
- The comma is the separator between key/value pairs
- There are 6 data types in JSON
- String - should be enclosed with double quotes like keys
- Number - can be integer, floating point, or exponential format
- Boolean - can only be true or false, without quotes, no capitals
- Null - this is an intentional omission of a value
- Array - this is called a list in python and Tcl
- Object - this is called a dictionary in python and Tcl
- Objects can be nested. (If you've ever pulled stats from iControl REST, you know this to be true!)
Creating a JSON test harness
Since iControl REST is JSON based, I could easily pass payloads from my desktop through a virtual server and onward to an internal host for the iControl REST endpoints, but I wanted something I could simplify with a pre-defined client and server payload. So I vibe coded a python script to do just that if you want to use it. I have a ubuntu desktop connected to both the client and server networks of the v21 BIG-IP in my lab. First I tested on localhost, then got my BIG-IP set up to handle the traffic as well.
Local test
Clientside
jrahm@udesktop:~/scripts$ ./cspayload.py client --host 10.0.3.95 --port 8088
[Client] Connecting to http://10.0.3.95:8088/
[Client] Sending JSON payload (POST):
{
"my_string": "Hello World",
"my_number": 42,
"my_boolean": true,
"my_null": null,
"my_array": [
1,
2,
3
],
"my_object": {
"nested_string": "I'm nested",
"nested_array": [
"a",
"b",
"c"
]
}
}
[Client] Received response (Status: 200):
{
"message": "Hello from server",
"type": "response",
"status": "success",
"data": {
"processed": true,
"timestamp": "2026-01-29"
}
}
Serverside
jrahm@udesktop:~/scripts$ ./cspayload.py server --host 0.0.0.0 --port 8088
[Server] Starting HTTP server on 0.0.0.0:8088
[Server] Press Ctrl+C to stop
[Server] Received JSON payload:
{
"my_string": "Hello World",
"my_number": 42,
"my_boolean": true,
"my_null": null,
"my_array": [
1,
2,
3
],
"my_object": {
"nested_string": "I'm nested",
"nested_array": [
"a",
"b",
"c"
]
}
}
[Server] Sent JSON response:
{
"message": "Hello from server",
"type": "response",
"status": "success",
"data": {
"processed": true,
"timestamp": "2026-01-29"
}
}
Great, my JSON payload is properly flowing from client to server on localhost. Now let's get the BIG-IP setup to manage this traffic.
BIG-IP config
This is a pretty basic setup, just need a JSON profile on top of the standard HTTP virtual server setup. My server is listening on 10.0.3.95:8088, so i'll add that as a pool member and then create the virtual in my clientside network at 10.0.2.50:80. Config is below.
ltm virtual virtual.jsontest {
creation-time 2026-01-29:15:10:10
destination 10.0.2.50:http
ip-protocol tcp
last-modified-time 2026-01-29:16:21:58
mask 255.255.255.255
pool pool.jsontest
profiles {
http { }
profile.jsontest { }
tcp { }
}
serverssl-use-sni disabled
source 0.0.0.0/0
source-address-translation {
type automap
}
translate-address enabled
translate-port enabled
vlans {
ext
}
vlans-enabled
vs-index 2
}
ltm pool pool.jsontest {
members {
10.0.3.95:radan-http {
address 10.0.3.95
session monitor-enabled
state up
}
}
monitor http
}
ltm profile json profile.jsontest {
app-service none
maximum-bytes 3000
maximum-entries 1000
maximum-non-json-bytes 2000
}
BIG-IP test, just traffic, no iRules yet
Ok, let's repeat the same client/server test to make sure we're flowing properly through the BIG-IP. I'll just show the clientside this time as the serverside would be the same as before. Note the updated IP and port in the client request should match the virtual server you create.
jrahm@udesktop:~/scripts$ ./cspayload.py client --host 10.0.2.50 --port 80
[Client] Connecting to http://10.0.2.50:80/
[Client] Sending JSON payload (POST):
{
"my_string": "Hello World",
"my_number": 42,
"my_boolean": true,
"my_null": null,
"my_array": [
1,
2,
3
],
"my_object": {
"nested_string": "I'm nested",
"nested_array": [
"a",
"b",
"c"
]
}
}
[Client] Received response (Status: 200):
{
"message": "Hello from server",
"type": "response",
"status": "success",
"data": {
"processed": true,
"timestamp": "2026-01-29"
}
}
Ok. Now we're cooking and BIG-IP is managing the traffic. Part two will drop as soon as I can share some crazy good news about a little thing happening at AppWorld you don't want to miss!
Help guide the future of your DevCentral Community!
What tools do you use to collaborate? (1min - anonymous)