Quick Tips
3 min

JSONPath for API Testing

Extract and validate data from JSON responses using JSONPath

...
jsonjsonpathapi-testing

JSONPath for API Testing

Sample JSON Response

{
  "status": "success",
  "data": {
    "user": {
      "id": 12345,
      "name": "John Doe",
      "email": "john@example.com",
      "roles": ["admin", "user"],
      "settings": {
        "notifications": true,
        "theme": "dark"
      }
    },
    "orders": [
      {
        "id": "ORD-001",
        "total": 99.99,
        "status": "completed",
        "items": [
          {"name": "Product A", "price": 49.99},
          {"name": "Product B", "price": 50.00}
        ]
      },
      {
        "id": "ORD-002",
        "total": 149.99,
        "status": "pending",
        "items": [
          {"name": "Product C", "price": 149.99}
        ]
      }
    ]
  }
}

1. Basic JSONPath Syntax

// Root element
$
 
// Direct child
$.status                    // "success"
$.data.user.name           // "John Doe"
 
// Array access by index
$.data.orders[0]           // First order
$.data.orders[0].id        // "ORD-001"
$.data.orders[-1]          // Last order
 
// All elements in array
$.data.orders[*]           // All orders
$.data.orders[*].status    // All order statuses

2. Array and Filter Operations

// Array slice
$.data.orders[0:2]         // First 2 orders
$.data.orders[1:]          // All except first
$.data.orders[-2:]         // Last 2 orders
 
// Filter by value
$.data.orders[?(@.status == 'completed')]
$.data.orders[?(@.total > 100)]
$.data.orders[?(@.status != 'cancelled')]
 
// Multiple conditions
$.data.orders[?(@.status == 'completed' && @.total > 50)]
$.data.orders[?(@.status == 'pending' || @.status == 'processing')]
 
// Filter and extract
$.data.orders[?(@.status == 'completed')].id
// Result: ["ORD-001"]

3. Using in REST Assured (Java)

import io.restassured.RestAssured;
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;
 
public class APITest {
    
    @Test
    public void testJSONPath() {
        given()
            .baseUri("https://api.example.com")
        .when()
            .get("/user/12345")
        .then()
            .statusCode(200)
            // Extract values
            .body("status", equalTo("success"))
            .body("data.user.name", equalTo("John Doe"))
            .body("data.user.email", containsString("@example.com"))
            
            // Array size
            .body("data.orders.size()", equalTo(2))
            .body("data.user.roles.size()", greaterThan(0))
            
            // Array contains
            .body("data.user.roles", hasItem("admin"))
            .body("data.orders.id", hasItems("ORD-001", "ORD-002"))
            
            // Filter and validate
            .body("data.orders.findAll { it.status == 'completed' }.size()", 
                  equalTo(1))
            .body("data.orders.find { it.total > 100 }.id", 
                  equalTo("ORD-002"));
    }
    
    @Test
    public void extractAndUseValue() {
        // Extract value for later use
        String userId = 
            given()
                .baseUri("https://api.example.com")
            .when()
                .post("/users")
            .then()
                .statusCode(201)
            .extract()
                .path("data.user.id");
        
        // Use extracted value
        given()
            .baseUri("https://api.example.com")
            .pathParam("id", userId)
        .when()
            .get("/users/{id}")
        .then()
            .statusCode(200);
    }
}

4. Using in Postman

// Tests tab in Postman
pm.test("Status is success", function() {
    var jsonData = pm.response.json();
    pm.expect(jsonData.status).to.eql("success");
});
 
pm.test("User name is correct", function() {
    var jsonData = pm.response.json();
    pm.expect(jsonData.data.user.name).to.eql("John Doe");
});
 
pm.test("Has admin role", function() {
    var jsonData = pm.response.json();
    pm.expect(jsonData.data.user.roles).to.include("admin");
});
 
pm.test("First order is completed", function() {
    var orders = pm.response.json().data.orders;
    pm.expect(orders[0].status).to.eql("completed");
});
 
// Using JSONPath (requires jsonpath library)
pm.test("All completed orders", function() {
    var jsonData = pm.response.json();
    var completed = jsonpath.query(jsonData, 
        "$.data.orders[?(@.status=='completed')]");
    pm.expect(completed.length).to.be.above(0);
});
 
// Save to environment variable
var token = pm.response.json().data.user.token;
pm.environment.set("auth_token", token);

5. Using in Python (requests + jsonpath-ng)

import requests
from jsonpath_ng import parse
 
response = requests.get("https://api.example.com/user/12345")
data = response.json()
 
# Basic extraction
status = parse('$.status').find(data)[0].value
print(f"Status: {status}")
 
# Nested value
user_name = parse('$.data.user.name').find(data)[0].value
print(f"Name: {user_name}")
 
# Array values
order_ids = [match.value for match in 
             parse('$.data.orders[*].id').find(data)]
print(f"Order IDs: {order_ids}")
 
# Filter (using jsonpath-ng.ext)
from jsonpath_ng.ext import parse as parse_ext
 
completed_orders = parse_ext(
    '$.data.orders[?(@.status=="completed")]'
).find(data)
 
for order in completed_orders:
    print(f"Completed Order: {order.value['id']}")

6. Common API Testing Patterns

Validate Response Structure:

.then()
    .body("status", notNullValue())
    .body("data", notNullValue())
    .body("data.user", hasKey("id"))
    .body("data.user", hasKey("email"))
    .body("data.orders", instanceOf(List.class));

Validate All Array Elements:

// All orders have required fields
.body("data.orders.id", everyItem(notNullValue()))
.body("data.orders.total", everyItem(greaterThan(0.0)))
.body("data.orders.status", 
      everyItem(isIn(Arrays.asList("pending", "completed", "cancelled"))));

Extract and Compare:

// Extract multiple values
List<String> orderIds = 
    response.jsonPath().getList("data.orders.id");
 
List<Double> totals = 
    response.jsonPath().getList("data.orders.total", Double.class);
 
// Validate
Assert.assertTrue(orderIds.size() > 0);
Assert.assertTrue(totals.stream().allMatch(t -> t > 0));

Deep Nested Extraction:

// Get all product names from all orders
List<String> productNames = 
    response.jsonPath().getList("data.orders.items.flatten().name");
 
// Sum of all prices
List<Double> allPrices = 
    response.jsonPath().getList("data.orders.items.flatten().price");
double total = allPrices.stream().mapToDouble(Double::doubleValue).sum();

7. Advanced Filters

// Regex match
$.data.orders[?(@.id =~ /ORD-.*/)]
 
// Contains
$.data.orders[?(@.status in ['pending', 'processing'])]
 
// Exists
$.data.orders[?(@.trackingNumber)]
 
// Multiple conditions with complex logic
$.data.orders[?(
    @.status == 'completed' && 
    @.total > 50 && 
    @.items.length > 1
)]
 
// Nested filter
$.data.orders[?(@.items[?(@.price > 100)])]

8. JSONPath in JMeter

JSON Path Extractor:
 
Names of created variables: orderId
JSON Path expressions: $.data.orders[0].id
Match No: 1
Default Values: NOT_FOUND
 
JSON Assertion:
 
Assert JSON Path exists: $.status
Additionally assert value: true
Expected Value: success

Quick Reference

Common JSONPath Expressions

ExpressionDescriptionExample Result
$RootEntire object
$.fieldDirect childField value
$..fieldRecursive descentAll field occurrences
$[0]Array indexFirst element
$[-1]Last elementLast element
$[*]All elementsAll array items
$[0:2]Array sliceFirst 2 elements
$[?(@.field)]Filter existsItems with field
$[?(@.x > 5)]Filter valueItems where x > 5

Operators in Filters

OperatorDescription
==Equal
!=Not equal
<, >, <=, >=Comparison
&&AND
`
=~Regex match
inIn array

Key Takeaways

  • Use $ to start from root of JSON
  • Use . for nested field access
  • Use [*] to get all array elements
  • Use [?(@.field)] to filter arrays
  • Combine filters with && and ||
  • Extract values for use in subsequent requests
  • Validate array sizes and element properties
  • Test for field existence with hasKey() or filters

Comments (0)

Loading comments...