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.

  1. Object delimeters are the curly brackets you see on lines 1 and 11, but also in the nested object in lines 7 and 10.
  2. 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.
  3. The colon is the separator between the key and its value
  4. The comma is the separator between key/value pairs
  5. 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
  6. 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!

 

 

Published Feb 19, 2026
Version 1.0
No CommentsBe the first to comment