Reference nested JSON arrays and objects in #each loop for Postman Visualizer

I’m trying to utilize the visulizer module to display information from a JSON response.
I’m using the example code from the documentation creating a table. In the example the blob is:

{
    	"contacts": [
    		{
    			"name": "{{$randomFullName}}",
    			"email": "{{$randomEmail}}"
    		},
    		{
    			"name": "{{$randomFullName}}",
    			"email": "{{$randomEmail}}"
    		},
    		{
    			"name": "{{$randomFullName}}",
    			"email": "{{$randomEmail}}"
    		},
    		{
    			"name": "{{$randomFullName}}",
    			"email": "{{$randomEmail}}"
    		}
    		]
    }

And the test code references the “name” objects simply with {{name}} in the #each loop for response.data.contracts

My JSON looks something like this:

{
	"payload": [{
			"object1": 0,
			"object2": 0,
			"children": [{
				"childobject1": 1,
				"childobject2": 2,
				"children": [{
					"childobject-a": 10,
					"childobject-b": 20
				}],
				"childobject4": 3,
				"pool_member": [{
					"childobject-x": 4,
					"childobject-y": 5
				}],
				"childobject5": 8
			}],
			"object4": 100,
			"object5": 110,
			"objectkey": "test1"
		},
		{
			"object1": 0,
			"object2": 0,
			"children": [{
				"childobject1": 1,
				"childobject2": 2,
				"children": [{
					"childobject-a": 10,
					"childobject-b": 20
				}],
				"childobject4": 3,
				"pool_member": [{
					"childobject-x": 4,
					"childobject-y": 5
				}],
				"childobject5": 8
			}],
			"object4": 100,
			"object5": 110,
			"objectkey": "test2"
		}
	]
}

But I can’t reference for example {{children.childobject2}} (nor using {{children[0].childobject2}} ) in an #each loop for response.payload inside my template declaration.
I suspect it has something to do with children being another array, but I can’t find the right syntax for this. Any hint as to how this can be accomplished?

Hey @jonasmellander

Welcome to the community! :wave:

This might be what you’re after:

var template = `
    <table>
        <tr>
            <th>A catchy title :)</th>
        </tr>
        {{#each response.payload}}
        {{#each children}}
        <tr>
            <td>{{this.childobject2}}</td>
        </tr>
        {{/each}}
        {{/each}}
    </table>
`;

pm.visualizer.set(template, {
    response: pm.response.json()
});

Your example request body isn’t valid JSON so I needed to fix that but this created the table in the visualizer with the number 2 in each of the table rows.

Thanks @dannydainton
Yes, that might be worth a try. I was looking at nested #each loops, but wanted to also use the {{@key}} counter, and couldn’t figure out if that is somehow doable with nested #each loops. Is there a way to call the the loop counters independently so to speak?
Thanks, Jonas

(I have corrected the JSON in my first post now. It was not the actual JSON, and I was only trying to show the example.)
@dannydainton I’ve taken your example code, and indeed it works. However, when I try my real example it still doesn’t work. But that’s surely cause it doesn’t quite match my first example JSON :slight_smile:

I have created another example in the form of a Postman collection for completeness, but as a beginner here in the forum, I’m not allowed to make attachments, so I guess one way to bypass that is to post the collection JSON as is instead. Just over 100 lines, so I hope it’s not seen as too offending. See the next post.

{
	"info": {
		"_postman_id": "340a7bee-a8ff-46af-bc52-23dcb60edb99",
		"name": "Print Tree Test Example",
		"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
	},
	"item": [
		{
			"name": "Print Tree Test",
			"event": [
				{
					"listen": "test",
					"script": {
						"id": "514dbe5d-4898-4ea6-a724-e64a75437ed0",
						"exec": [
							"var template = `",
							"    <style type=\"text/css\">",
							"        .tftable {font-size:14px;color:#333333;width:100%;border-width: 1px;border-color: #87ceeb;border-collapse: collapse;}",
							"        .tftable th {font-size:18px;background-color:#87ceeb;border-width: 1px;padding: 8px;border-style: solid;border-color: #87ceeb;text-align:left;}",
							"        .tftable tr {background-color:#ffffff;}",
							"        .tftable td {font-size:14px;border-width: 1px;padding: 8px;border-style: solid;border-color: #87ceeb;}",
							"        .tftable tr:hover {background-color:#e0ffff;}",
							"    </style>",
							"    ",
							"    <table class=\"tftable\" border=\"1\">",
							"        <tr>",
							"            <th>Main instance</th>",
							"            <th>Pool</th>",
							"            <th>Children</th>",
							"        </tr>",
							"        ",
							"        {{#each response.data.payload}}",
							"            <tr >",
							"\t            <td rowspan={{this.children.childcount}}>{{instancename}}</td>",
							"\t            <td rowspan={{this.children.childcount}}>{{objectkey}}</td>",
							"                <td>",
							"                    <table class=\"tftable\" border=\"0\">",
							"                        {{#each this.children.pool_member}}",
							"\t                    <tr><td >{{this.childobject-x}}</td></tr>",
							"                        {{/each}}",
							"                    </table>",
							"                </td>",
							"            </tr>",
							"        {{/each}}",
							"    </table>",
							"`;",
							"",
							"",
							"pm.visualizer.set(template, {",
							"    response: pm.response.json()",
							"});",
							""
						],
						"type": "text/javascript"
					}
				}
			],
			"protocolProfileBehavior": {
				"disabledSystemHeaders": {
					"content-type": true
				}
			},
			"request": {
				"auth": {
					"type": "noauth"
				},
				"method": "POST",
				"header": [
					{
						"key": "Content-Type",
						"type": "text",
						"value": "application/json"
					},
					{
						"key": "Accept",
						"type": "text",
						"value": "application/json",
						"disabled": true
					}
				],
				"body": {
					"mode": "raw",
					"raw": "{\n\t\"payload\": [\n\t\t{\n\t\t\t\"instancename\": \"object0value_0\",\n\t\t\t\"object2\": 2,\n\t\t\t\"children\": [\n\t\t\t\t{\n\t\t\t\t\t\"childobject1\": 1,\n\t\t\t\t\t\"childobject2\": \"childobject2value_0\",\n\t\t\t\t\t\"children\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"childobject-a\": \"childObject-a_0_2\",\n\t\t\t\t\t\t\t\"childobject-b\": 20\n\t\t\t\t\t\t}\n\t\t\t\t\t\t],\n\t\t\t\t\t\"childobject4\": 3,\n\t\t\t\t\t\"pool_member\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\"childobject-x\": \"childObject-x_0_2\",\n\t\t\t\t\t\t\"childobject-y\": 5\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"childcount\": 1\n\t\t\t\t}\n\t\t\t],\n\t\t\t\"object4\": 100,\n\t\t\t\"object5\": 1,\n\t\t\t\"objectkey\": \"test1\"\n\t\t},\n\t\t{\n\t\t\t\"instancename\": \"object1value_0\",\n\t\t\t\"object2\": 0,\n\t\t\t\"children\": [\n\t\t\t\t{\n\t\t\t\t\t\"childobject1\": 1,\n\t\t\t\t\t\"childobject2\": \"childobject2value_1\",\n\t\t\t\t\t\"children\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"childobject-a\": \"childObject-a_1_2\",\n\t\t\t\t\t\t\t\"childobject-b\": 20\n\t\t\t\t\t\t}\n\t\t\t\t\t\t],\n\t\t\t\t\t\"childobject4\": 3,\n\t\t\t\t\t\"pool_member\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\"childobject-x\": \"childObject-x_1_2\",\n\t\t\t\t\t\t\"childobject-y\": 5\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\"childobject-x\": \"childObject-x_1_3\",\n\t\t\t\t\t\t\"childobject-y\": 6\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"childcount\": 2\n\t\t\t\t}\n\t\t\t],\n\t\t\t\"object4\": 100,\n\t\t\t\"object5\": 2,\n\t\t\t\"objectkey\": \"test2\"\n\t\t}\n\t]\n}\n\t\t"
				},
				"url": {
					"raw": "https://postman-echo.com/post",
					"protocol": "https",
					"host": [
						"postman-echo",
						"com"
					],
					"path": [
						"post"
					]
				}
			},
			"response": []
		}
	],
	"protocolProfileBehavior": {}
}

Hey @jonasmellander

This extra data you have here response.data.payload isn’t part of your response.

It’s part of the response for the visualizer example so that’s why it’s in there.

Hi @dannydainton
It’s part of the response when running this very collection and request, is it not? I added it for that very reason, and the first two columns in my HTML table do get correctly populated in the Visualizer. Or am I missing something after all?

If you look at the HTML source code in the Visualizer the second TR tag in the table has TD tags where the attributes somehow even went from “rowspan=” to just “rowspan” with the equal to sign removed as well. Not sure what causes not only the variable to be empty, but the character before it to be removed.
I think that if we get it working in this very collection/request, I’ll be able to get it to work in my real example, as this is more identical to what I’m observing.
Thanks for your help and efforts so far! Much appreciated.

Not really sure what you were trying to do with rowspan there and you also had a table, inside a table.

var template = `
    <style type="text/css">
        .tftable {font-size:14px;color:#333333;width:100%;border-width: 1px;border-color: #87ceeb;border-collapse: collapse;}
        .tftable th {font-size:18px;background-color:#87ceeb;border-width: 1px;padding: 8px;border-style: solid;border-color: #87ceeb;text-align:left;}
        .tftable tr {background-color:#ffffff;}
        .tftable td {font-size:14px;border-width: 1px;padding: 8px;border-style: solid;border-color: #87ceeb;}
        .tftable tr:hover {background-color:#e0ffff;}
    </style>
    
    <table class="tftable" border="1">
        <tr>
            <th>Main instance</th>
            <th>Pool</th>
            <th>Children</th>
        </tr>
        
        {{#each response.data.payload}}
            <tr>
	            <td>{{instancename}}</td>
	            <td>{{objectkey}}</td>

                {{#each children}}
                {{#each pool_member}}
	                <td rowspan=1>{{childobject-x}}</td>
                {{/each}}
                {{/each}}
            </tr>
        {{/each}}
    </table>
`;


pm.visualizer.set(template, {
    response: pm.response.json()
});

With the shape of that data - It wouldn’t really look right with the current visualization:

What would like the final visualization to look like? Could you provide a mock image?

Thanks @dannydainton
I’ll try that out. The Rowspan was originally to make the cells of the first two columns span across multiple rows, depending on the number of children in the pool. (There’s always one main, most of the time one pool and 0-to-many children per pool.) But when I realized it was difficult to make the equivalent looping to create that HTML code (as you create new TableRows for each child cell) I redesigned and just put them all in a single nested table. (I know it’s probably ugly from a modern HTML point of view (My HTML skills stem from then last millenium :slight_smile: )
But I’ll have a look at your example code and report back. I don’t believe I’m able to attach images just yet. Too much of a forum newbie :wink:

Does this not work for you?

You’re right. I missed that button, which looks a little different in my end compared to the picture in that post. Is it only for images?

Anyways, your new code did help me to an acceptable result. Thank you very much!

<table class="tftable">
    <tr>
        <th>Main instance</th>
        <th>Pool</th>
        <th>Children</th>
    </tr>

{{#each response.data.payload}}
    <tr>
            <td>{{instancename}}</td>
            <td>{{objectkey}}</td>
        <td>
            <table class="tftable">
                {{#each children}}
                {{#each pool_member}}
                    <tr><td>{{this.childobject-x}}</td></tr>
                {{/each}}
                {{/each}}
            </table>
        </td>
    </tr>
{{/each}}

The idea was originally to create something like this:

<HTML>
<BODY>
<style type="text/css">
        .tftable {font-size:14px;color:#333333;width:100%;border-width: 1px;border-color: #87ceeb;border-collapse: collapse;}
        .tftable th {font-size:18px;background-color:#87ceeb;border-width: 1px;padding: 1px;border-style: solid;border-color: #87ceeb;text-align:left;}
        .tftable tr {background-color:#ffffff;}
        .tftable td {font-size:14px;border-width: 1px;padding: 8px;border-style: solid;border-color: #87ceeb;}
        .tftable tr:hover {background-color:#e0ffff;}
    </style>
    
    <table class="tftable" border="1">
        <tr>
            <th>Main instance</th>
            <th>Pool</th>
            <th>Children</th>
        </tr>
        
            <tr id=row_1>
                <td id=1 rowspan=2>object0value_0</td>
                <td rowspan=2>test1</td>                
                <td id=subrow_1>childObject-x_0_2</td>
            </tr>
            <tr>
                <td id=subrow_2>childObject-x_0_3</td>
            </tr>
            <tr id=row_2>
                <td id=1 rowspan=3>object1value_0</td>
                <td rowspan=3>test2</td>                
                <td id=subrow_1>childObject-x_1_2</td>
                </tr>
            <tr>
                <td id=subrow_2>childObject-x_1_3</td>
            </tr>
            <tr>
                <td id=subrow_3>childObject-x_1_4</td>
			</tr>

    </table>
    </BODY>
    </HTML>

This is the two outputs (of course not identical data, but still):


How about this as an alternative:

var template = `
<html>
<body>
<style type="text/css">
    .tftable {font-size:14px;color:#333333;width:100%;border-width: 1px;border-color: #87ceeb;border-collapse: collapse;}
    .tftable th {font-size:18px;background-color:#87ceeb;border-width: 1px;padding: 1px;border-style: solid;border-color: #87ceeb;text-align:left;}
    .tftable tr {background-color:#ffffff;}
    .tftable td {font-size:14px;border-width: 1px;padding: 8px;border-style: solid;border-color: #87ceeb;}
    .tftable tr:hover {background-color:#e0ffff;}
</style>
    
<table class="tftable" border="1">
    <tr>
        <th>Main instance</th>
        <th>Pool</th>
        <th>Children</th>
    </tr>
    {{#each response.data.payload}}
    <tr>
        <td>{{instancename}}</td>
        <td>{{objectkey}}</td>
        <td>
            <table border="1">
            <tr>
                {{#each children}}
                {{#each pool_member}}
	                <tr><td>{{childobject-x}}</td></tr>
                {{/each}}
                {{/each}}
            </table>
        </td>
    </tr>
    {{/each}}
</table>
</body>
</html>
`

pm.visualizer.set(template, {
    response: pm.response.json()
});

Yes, that’s pretty much what I had in my last post, isn’t it? It’s OK, but not quite as neat as the kind of table I really wanted as an output.

I tried an ugly trick trying to just JavaScript document.write, but somehow it’s not working. But I suspect that there are some limitations to the javascript that can be run in the visualizer. Or I’m simply missing some detail.
But I find it strange that the </tr> after the #each loop on the left side is simply omitted in the code shown in the code inspector on the right.