Visualizers for xml api response body

looking for some visualizer options for API XML response, mainly want to convert the data into a bar/line graph. I did attempt to convert the xml to json with the post test script then use a visualizer template for json. This did not work and gave me some errors (JSONError: Unexpected token โ€˜<โ€™ at 1:1 ^).

Can anyone point me to some good documentation/examples for what I am attempting to perform?

Thank you

@porfiiro.alvarado: Not sure you have this already. Else please check the below links, and try to export the sample collection:

https://explore.postman.com/templates/4424/codebase-visualizer-feature-d3-templates

https://explore.postman.com/templates/4220/visualizer-table

Hey @porfiiro.alvarado

Welcome to the community! :wave:

That seems like an error with a part of your test script, would you be able to share the code and an example of the response body please?

Thank you both for the response. Iโ€™m using test script from (visualizer features Templates) open Brewery BD and modifying the predefined fields.

basically trying to get the user count by device type.

    <domain></domain>
    <islocal>no</islocal>
    <username>email</username>
    <primary-username>person>
    <region-for-config>LC</region-for-config>
    <source-region>LC</source-region>
    <computer>name</computer>
    <client>devicetype</client>

Whatโ€™s the script that you have so far?

Is that the whole response body?

looking at the error and the xml api reponse seems its the first resposne that is breaking it.

There was an error in evaluating the test script: JSONError: Unexpected token โ€˜<โ€™ at 1:1 ^

no email person> LC LC name devicetype

Whatโ€™s the script that you have so far?

Is that the whole response body?

Screen Shot 2020-09-04 at 9.36.45 AM this is the response i get for my API call which is xml, the error that postman gives me is, this is line one of my response.
Screen Shot 2020-09-04 at 9.38.22 AM

attached is the rough sample script that i am still modifying

If you have code, could you copy and paste the code please rather than an image of it. It allows people to use something like a mock server to recreate this locally.

With images, people to have to manually type out every line in the code from the image, into Postman :grimacing:

Does the response body have closing brackets for entry and result? Can you make sure youโ€™re including the full response body so that nothing is missed. This is quite important when it comes to the parsing the data thatโ€™s there.

Even from the image, I can see that youโ€™re trying to parse an XML response body as JSON so thatโ€™s why you get the JSONError that you can see.

code below, sorry about that at first it would not let me past the source code which is the reason for the images.

var jsonObject = xml2Json(responseBody);
/* VISUALIZATION TEMPLATE */
var template = `
<script src="https://d3js.org/d3.v5.js"></script>
<div id="clientChart"></div>
<style>
    #barChart {
        width:700px;
        background-color: #F5F5F5;
    }
    .grid line {
        stroke: lightgrey;
        shape-rendering: crispEdges
    }
</style>
<script>

    pm.getData( function(err, value){ 
        const xAxisLabel = value.xAxisLabel;
        const yAxisLabel = value.yAxisLabel;
        const title = value.title;
        const data = value.data;
        
        var margin = {top: 75, right: 200, bottom: 60, left: 90},
            width = 650 - margin.left - margin.right,
            height = 450 - margin.top - margin.bottom;
        
        // set the ranges
        var x = d3.scaleBand()
            .domain(data.map(function(d){return d.key;}))
            .range([0, width])
            .padding(0.1);
        var y = d3.scaleLinear()
            .domain([0, d3.max(data, function(d){return d.frequency;})])
            .range([height, 0]);
        const colorScale = d3.scaleOrdinal(d3.schemeCategory10);
        
        // append the svg object to the body of the page
        // append a 'group' element to 'svg'
        // moves the 'group' element to the top left margin
        var svg = d3.select("#barChart").append("svg")
            .attr("width", width + margin.left + margin.right)
            .attr("height", height + margin.top + margin.bottom)
            .append("g")
            .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
        
        function make_y_gridlines() {
            return d3.axisLeft(y)
        //      .ticks(d3.max(data, function(d){return d.frequency;}));
                .ticks(10);
        }
      
        svg.append("g")
            .attr("class","grid")
            .call(make_y_gridlines()
                .tickSize(-width)
                .tickFormat("")
            );
        
        // create hover tooltip
        let tooltip = d3.select("#barChart").append("div")
            .attr("class", "tooltip")
            .style("position", "absolute")
            .style("font-size", "12px")
            .style("width", "auto")
            .style("height", "auto")
            .style("pointer-events", "none")
            .style("background-color", "white")
            .style("padding", "3px")
            .style("opaclient", 0);
        
        // tooltip mouseover event handler
        let tipMouseover = function(d){
            tooltip.html(xAxisLabel + ": <b>" + d.key + "</b><br/>" + yAxisLabel + ": <b>" + d.frequency + "</b>")
                .style("left", (d3.event.pageX + 15) + "px")
                .style("top", (d3.event.pageY - 20) + "px")
            .transition()
                .duration(200)  // in ms
                .style("opaclient", 0.9)
        };
        
        // tooltip mouseout event handler
        let tipMouseout = function(d){
            tooltip.transition()
                .duration(300)
                .style("opaclient", 0);
        };
            
        // append the rectangles for the bar chart
        svg.selectAll(".bar")
        .data(data)
        .enter().append("rect")
            .on("mouseover", tipMouseover)
            .on("mouseout", tipMouseout)
        .attr("class", "bar")
        .attr("x", (function(d) { return x(d.key); }))
        .attr("width", x.bandwidth())
        .attr("y", (function(d) { return y(d.frequency); }))
        .attr("height", (function(d) { return height - y(d.frequency); }))
        .style('fill', (d, i) => colorScale(i));
        
        // add the x Axis
        svg.append("g")
        .attr("transform", "translate(0," + height + ")");
    
        // add the y-axis
        svg.append("g")
        .call(d3.axisLeft(y));
        var fontSize = 24;
        var legendSpace = 25;
        var legendX = 475;
        var legendY = 30;
        var legendTitle = xAxisLabel;
        var legend = svg.append('g')
            .attr('class', 'legend')
            .attr('transform', function(d, i) { return "translate(" + legendX + ", " + legendY + ")"; });
    
        // add legend title
        legend.append("text")
            .attr("fill", d =>"#00000")
            .style("font-size", "18px")
            .text(legendTitle);
            
        var legendItems = legend.selectAll("g")
            .data(data).enter()
                .append("g")
                    .attr("class", "item")
                    .attr("transform", function(d, i){ return "translate(-30, " + (i*legendSpace + fontSize) + ")"; });
        
        legendItems.append("circle")
            .attr("r", 5)
            .attr("fill", (d, i) => colorScale(i))
        
        legendItems.append("text")
            .attr("transform", function(d){ return "translate(" + legendSpace + ", 0)"; })
            .attr("fill", d =>"#00000")
            .attr("alignment-baseline","middle")
            .text(function(d) { return d.key; });
        
        // create axes
        svg.append("text")
                .attr("transform", "translate(" + (width/2) + " ," + (height + 32) + ")")
                .style("text-anchor", "middle")
                .text(xAxisLabel);
                
        svg.append("text")
            .attr("transform", "rotate(-90)")
            .attr("y", 15 - margin.left)
            .attr("x", 0 - (height/2))
            .attr("dy", "1em")
            .style("text-anchor", "middle")
            .text(yAxisLabel);
        
        // create graph title
        svg.append("text")
            .attr("transform", "translate(" + (width/2) +" ," + -30 + ")")
            .attr("font-weight", 600)
            .text(title);
    });

</script>`;

var response = pm.response.json();

/* DATA PARSING */

// Construct a map from each client to the number of occurrences
let clientFrequency = {};
let client = "";
for (let client of response) {
    client = client;
    if (client in clientFrequency) {
        clientFrequency[client] += 1;
    } else {
        clientFrequency[client] = 1;
    }
}

// Number of client to be displayed in client chart
const numclient = 8;

// Function to return the name of the client with the maximum value
findMax = array => array.reduce((a, b) => clientFrequency[a] > clientFrequency[b] ? a : b, gpclient[0]);

// Find the numclient gpclient with the most occurrences
let gpclient = Object.keys(clientFrequency);
let maxgpclient = {};
if (gpclient.length < numclient) {
    maxgpclient = clientFrequency;
} else {
    let maxclient = "";
    for (let i = 0; i < numclient; i++) {
        gpclient = Object.keys(clientFrequency);
        maxclient = findMax(gpclient);
        maxgpclient[maxclient] = clientFrequency[maxclient];
        delete clientFrequency[maxclient];
    }
}


// Format into the final structure, an array of {key: string, frequency: int} objects
const resList = [];
for (let [c, f] of Object.entries(maxgpclient)) {
    resList.push({
   "key": c,
   "frequency": f
    });
}

/* SET LABELS */
const xAxis = "client type";
const yAxis = "GP Clients";
const title = "Client Count";

/* FEED DATA INTO TEMPLATE */
pm.visualizer.set(template, {
    xAxisLabel: xAxis,
    yAxisLabel: yAxis,
    title: title,
    data: resList
} );

below is a sample output, API response body starts with response status, starting with data the data i need for the visualizer is separated by which can be up to 500 items.

<response status="success"><result>
<entry>
	<domain></domain>
	<islocal>xxx</islocal>
	<username>xxxxxx</username>
	<primary-username>xxxxxx</primary-username>
	<region-for-config>xx</region-for-config>
	<source-region>xx</source-region>
	<computer>xxxxxxx</computer>
	<client>xxxxxx</client>
	<vpn-type>xxxx</vpn-type>
	<virtual-ip>xxxxx</virtual-ip>
	<virtual-ipv6>:xx :</virtual-ipv6>
	<public-ip>xxxxx</public-ip>
	<public-ipv6>: xx:</public-ipv6>
	<tunnel-type>xxxx</tunnel-type>
	<public-connection-ipv6>xx</public-connection-ipv6>
	<client-ip>xxxxx</client-ip>
	<login-time>xxxxx</login-time>
	<login-time-xxx>xxxxxx</login-time-utc>
	<lifetime>xxxxx</lifetime>
	<request-login>xxxxxx
(0), (0), : :