XML Reference

The XML object is available within Loadster Code Blocks and provides methods for parsing and working with XML data. This functionality allows you to extract data from XML responses, validate XML structure, and navigate complex XML documents programmatically.

Understanding XML Processing in Loadster

In Loadster, XML data is commonly encountered in SOAP web services, REST API responses, configuration files, and legacy system integrations. The XML object provides powerful parsing capabilities using the open source xmldoc library.

XML (eXtensible Markup Language) consists of elements (tags), attributes, and text content. For example:

<user id="123" active="true">
    <name>John Doe</name>
    <email>john@example.com</email>
</user>

In this example:

  • user is an element with attributes id="123" and active="true"
  • name and email are child elements containing text content
  • Elements can contain other elements, creating a hierarchical structure

All XML parsing methods are synchronous and work consistently across both Protocol Scripts and Browser Scripts. You’ll most commonly need to manually work with XML when using Protocol Scripts to test XML-based APIs and web services, though the XML object is available in any type of Loadster script. Element navigation is case-sensitive - “User” and “user” are different elements.

XML Document Parsing

These methods parse XML strings into document objects that you can navigate and query.

XML.parse(xmlString)

Parse an XML string and return an XML document object.

const xml = XML.parse(response.string());

// Parse XML from a string literal
const xmlDoc = XML.parse('<users><user id="1">John</user></users>');

// Parse XML from HTTP response
const response = http.get('https://api.example.com/data.xml');
const xml = XML.parse(response.string());

Parameters:

  • xmlString - The XML string to parse

This method parses an XML string and returns an XML document object that you can navigate using the methods below. The parser handles well-formed XML and will throw an error if the XML is malformed.

Note: XML parsing is synchronous - no need for await or promise chains. Always wrap XML parsing in try-catch blocks to handle malformed XML gracefully.

Basic XML Parsing Example

const xmlString = `
<users>
    <user id="1">
        <name>John Doe</name>
        <email>john@example.com</email>
    </user>
    <user id="2">
        <name>Jane Smith</name>
        <email>jane@example.com</email>
    </user>
</users>`;

const xml = XML.parse(xmlString);
const users = xml.childrenNamed("user");

console.log(`Found ${users.length} users`);

XML Element Navigation

These methods help you navigate through XML document structure to find specific elements and data.

childrenNamed(name)

Get all child elements with a specific name.

Given this XML:

<users>
    <user id="1">John</user>
    <user id="2">Jane</user>
    <user id="3">Bob</user>
</users>
const xml = XML.parse(xmlString); // Parse the XML first
const users = xml.childrenNamed("user");
console.log(users.length); // 3

// Iterate through all user elements
for (let i = 0; i < users.length; i++) {
    console.log(users[i].attr.id);
}

Parameters:

  • name - The element name to search for

This method returns an array of all direct child elements with the specified name. It only searches immediate children, not descendants at deeper levels.

Note: Methods like childrenNamed() return arrays, while childNamed() returns single elements or undefined if not found.

childNamed(name)

Get the first child element with a specific name.

Given this XML:

<response>
    <status>success</status>
    <user id="1">John</user>
    <user id="2">Jane</user>
</response>
const xml = XML.parse(xmlString); // Parse the XML first
const status = xml.childNamed("status");
console.log(status.val); // "success"

const firstUser = xml.childNamed("user");
console.log(firstUser.attr.id); // "1"

Parameters:

  • name - The element name to search for

This method returns the first direct child element with the specified name, or undefined if no such element exists. Use this when you expect only one element or only need the first match.

Note: Element navigation is case-sensitive - “User” and “user” are different elements.

childWithAttribute(name, value)

Get the first child element with a specific attribute value.

Given this XML:

<users>
    <user id="1" role="admin">John</user>
    <user id="2" role="user">Jane</user>
    <user id="3" role="user">Bob</user>
</users>
const xml = XML.parse(xmlString); // Parse the XML first
const adminUser = xml.childWithAttribute("role", "admin");
console.log(adminUser.val); // "John"

const specificUser = xml.childWithAttribute("id", "2");
console.log(specificUser.val); // "Jane"

Parameters:

  • name - The attribute name to match
  • value - The attribute value to match

This method returns the first direct child element that has an attribute with the specified name and value. It’s useful for finding elements by ID or other unique attributes.

descendantWithPath(path)

Get a descendant element using a path.

Given this XML:

<response>
    <data>
        <user>
            <profile>
                <name>John Doe</name>
                <contact>
                    <email>john@example.com</email>
                </contact>
            </profile>
        </user>
    </data>
</response>
const xml = XML.parse(xmlString); // Parse the XML first
const nameElement = xml.descendantWithPath("data.user.profile.name");
console.log(nameElement.val); // "John Doe"

const emailElement = xml.descendantWithPath("data.user.profile.contact.email");
console.log(emailElement.val); // "john@example.com"

Parameters:

  • path - Dot-separated path to the element (e.g., “parent.child.grandchild”)

This method navigates through nested elements using a dot-separated path. It’s useful for accessing deeply nested elements without chaining multiple childNamed() calls.

valueWithPath(path)

Get the text value of a descendant element.

Given this XML:

<order>
    <customer>
        <name>John Doe</name>
        <address>
            <street>123 Main St</street>
            <city>Anytown</city>
        </address>
    </customer>
    <total>99.99</total>
</order>
const xml = XML.parse(xmlString); // Parse the XML first
const customerName = xml.valueWithPath("customer.name");
console.log(customerName); // "John Doe"

const city = xml.valueWithPath("customer.address.city");
console.log(city); // "Anytown"

const total = xml.valueWithPath("total");
console.log(total); // "99.99"

Parameters:

  • path - Dot-separated path to the element whose text value you want

This method is a shortcut that combines descendantWithPath() and accessing the .val property. It returns the text content of the element at the specified path, or undefined if the path doesn’t exist.

XML Element Properties

These properties provide access to element attributes, text content, and metadata.

Element Attributes

Given this XML:

<product id="123" category="electronics" price="299.99" inStock="true">
    <name>Smartphone</name>
</product>
const xml = XML.parse(xmlString); // Parse the XML first
const product = xml.childNamed("product");
const productId = product.attr.id; // "123"
const category = product.attr.category; // "electronics"
const price = product.attr.price; // "299.99"
const inStock = product.attr.inStock; // "true"

// Check if attribute exists
if (product.attr.discount) {
    console.log("Product has discount:", product.attr.discount);
} else {
    console.log("No discount available");
}

Element attributes are accessed using the .attr property. All attribute values are returned as strings, so you may need to convert them to numbers or booleans as needed.

Note: Use the .attr property to access element attributes - all values are returned as strings regardless of their appearance in the XML.

Element Text Content

Given this XML:

<book>
    <title>JavaScript Guide</title>
    <author>Jane Smith</author>
    <description>A comprehensive guide to JavaScript programming.</description>
    <pages>350</pages>
</book>
const xml = XML.parse(xmlString); // Parse the XML first
const title = xml.childNamed("title");
const titleText = title.val; // "JavaScript Guide"

const author = xml.childNamed("author").val; // "Jane Smith"
const pages = xml.childNamed("pages").val; // "350" (string)

// Convert to number if needed
const pageCount = parseInt(xml.childNamed("pages").val); // 350 (number)

Element text content is accessed using the .val property. This returns the text content between the opening and closing tags as a string.

Note: Use the .val property to access element text content - all content is returned as strings.

Element Name

Given this XML:

<library>
    <book id="1">JavaScript Guide</book>
    <magazine id="2">Tech Today</magazine>
    <dvd id="3">Learning Videos</dvd>
</library>
const xml = XML.parse(xmlString); // Parse the XML first
const items = xml.children; // Get all child elements
for (let i = 0; i < items.length; i++) {
    const item = items[i];
    const elementName = item.name; // "book", "magazine", "dvd"
    const id = item.attr.id;
    const content = item.val;
    
    console.log(`${elementName} #${id}: ${content}`);
}

The .name property returns the element’s tag name as a string. This is useful when iterating through mixed element types or when you need to identify the type of element you’re working with.

XML Examples

These examples demonstrate typical XML processing scenarios in load testing.

Parse API Response

When testing XML-based APIs, you often need to extract user data from paginated responses. This example shows how to parse an API response that contains user information with metadata like pagination details. You’ll typically encounter this pattern when testing user management systems or customer databases.

// Parse XML response from an API
const response = http.get('https://api.example.com/users.xml');
const xml = XML.parse(response.string());

// Extract metadata
const totalCount = xml.attr.total;
const page = xml.attr.page;
console.log(`Page ${page}, ${totalCount} total users`);

// Process user data
const users = xml.childrenNamed("user");
for (let i = 0; i < users.length; i++) {
    const user = users[i];
    const id = user.attr.id;
    const name = user.childNamed("name").val;
    const email = user.childNamed("email").val;
    const isActive = user.attr.active === "true";
    
    console.log(`User ${id}: ${name} (${email}) - Active: ${isActive}`);
}

This pattern demonstrates how to extract both metadata (like pagination info) and individual record data from a structured XML response. The key is to handle both attributes and nested elements, converting string attributes to appropriate data types when needed.

Extract Specific Data for Variables

Load testing often requires extracting specific data from one API call to use in subsequent requests. This example shows how to find a particular user by ID and store their information in bot variables for later use in the test flow. This is essential for creating realistic user journeys that span multiple API calls.

// Extract specific information from XML and store in variables
const response = http.get('https://api.example.com/profile.xml');
const xml = XML.parse(response.string());

// Find user with specific ID
const userId = bot.getVariable("currentUserId");
const targetUser = xml.childWithAttribute("id", userId);

if (targetUser) {
    // Extract user details
    const userName = targetUser.childNamed("name").val;
    const userEmail = targetUser.childNamed("email").val;
    const userRole = targetUser.attr.role;
    
    // Store in bot variables for later use
    bot.setVariable("extractedUserName", userName);
    bot.setVariable("extractedUserEmail", userEmail);
    bot.setVariable("extractedUserRole", userRole);
    
    console.log(`Extracted user: ${userName} (${userRole})`);
} else {
    console.warn(`User with ID ${userId} not found`);
}

This approach ensures that your test can chain API calls together realistically, using data from one response to drive the next request. The null checking prevents test failures when the expected data isn’t found, and storing values in bot variables makes them available throughout the test execution.

Validate XML Structure and Content

Robust load testing requires validating that API responses contain the expected data structure and content. This example shows comprehensive XML validation that checks both the structural integrity and data quality of an XML response. This type of validation helps catch API changes or data corruption issues during load testing.

try {
    const xml = XML.parse(response.string());
    
    // Check root element
    if (xml.name !== "users") {
        console.error(`Expected root element 'users', got '${xml.name}'`);
        return false;
    }
    
    // Check if required elements exist
    const users = xml.childrenNamed("user");
    if (users.length === 0) {
        console.error("No users found in XML response");
        return false;
    }
    
    // Validate each user has required fields
    for (let i = 0; i < users.length; i++) {
        const user = users[i];
        
        // Check required attributes
        if (!user.attr.id) {
            console.error(`User ${i} missing id attribute`);
            return false;
        }
        
        // Check required child elements
        if (!user.childNamed("name")) {
            console.error(`User ${user.attr.id} missing name element`);
            return false;
        }
        
        if (!user.childNamed("email")) {
            console.error(`User ${user.attr.id} missing email element`);
            return false;
        }
        
        // Validate data format
        const email = user.childNamed("email").val;
        if (!email.includes("@")) {
            console.error(`User ${user.attr.id} has invalid email: ${email}`);
            return false;
        }
    }
    
    console.log(`XML validation passed: ${users.length} users`);
    return true;
    
} catch (error) {
    console.error("XML parsing failed:", error.message);
    return false;
}

This comprehensive validation approach ensures that your load test not only checks performance but also verifies data quality. By validating structure, required fields, and data formats, you can catch regressions in API behavior before they reach production. The detailed error messages help with debugging when validation fails.

SOAP Web Service Response Processing

Many enterprise systems still use SOAP web services, which return XML responses wrapped in SOAP envelopes. This example shows how to navigate through the SOAP structure to extract business data from a typical order status response. SOAP responses often include namespaces and nested structures that require careful navigation.

// Process SOAP response with namespaces and nested data
const soapResponse = http.post('https://api.example.com/soap', soapRequest, {
    headers: { 'Content-Type': 'text/xml', 'SOAPAction': 'getOrderStatus' }
});

const xml = XML.parse(soapResponse.string());

// Navigate through SOAP envelope structure
const body = xml.childNamed("soap:Body") || xml.childNamed("Body");
if (body) {
    const response = body.childNamed("GetOrderStatusResponse");
    if (response) {
        const order = response.childNamed("Order");
        
        // Extract order details
        const orderId = order.attr.id;
        const status = order.childNamed("Status").val;
        const amount = order.childNamed("Amount").val;
        const currency = order.childNamed("Amount").attr.currency;
        
        // Store extracted data
        bot.setVariable("orderId", orderId);
        bot.setVariable("orderStatus", status);
        bot.setVariable("orderAmount", amount);
        bot.setVariable("orderCurrency", currency);
        
        console.log(`Order ${orderId}: ${status} - ${amount} ${currency}`);
    }
}

SOAP processing requires understanding the envelope structure and being flexible with namespace handling. This pattern shows how to extract meaningful business data from SOAP responses while handling the protocol overhead. The extracted data can then be used to validate business logic or drive subsequent test steps.

Complex XML Navigation

Real-world XML APIs often return mixed content with different types of elements that require different processing logic. This example demonstrates how to handle a catalog response containing different item types (products, categories, promotions) each with their own data structure. This pattern is common in e-commerce and content management systems.

// Navigate complex XML with mixed content types
const xml = XML.parse(response.string());

// Process different types of elements
const items = xml.childrenNamed("item");
for (let i = 0; i < items.length; i++) {
    const item = items[i];
    const itemType = item.attr.type;
    
    switch (itemType) {
        case "product":
            const productName = item.childNamed("name").val;
            const price = parseFloat(item.childNamed("price").val);
            console.log(`Product: ${productName} - $${price}`);
            break;
            
        case "category":
            const categoryName = item.childNamed("name").val;
            const subItems = item.childrenNamed("subItem");
            console.log(`Category: ${categoryName} (${subItems.length} items)`);
            break;
            
        case "promotion":
            const discount = item.attr.discount;
            const validUntil = item.childNamed("validUntil").val;
            console.log(`Promotion: ${discount}% off until ${validUntil}`);
            break;
    }
}

This pattern demonstrates how to handle heterogeneous XML data where different elements require different processing logic. By using the element type or name to determine processing approach, you can handle complex APIs that return mixed content types in a single response. This approach scales well as new item types are added to the API.