Skip to content

Martini XML Functions

Overview

Martini's built-in XML functions let you convert, read, and write XML data directly inside your services and workflows—without external libraries or custom parsing code. Use these functions to transform data models to XML strings, parse XML from files and streams, stream large XML datasets one element at a time, and control fine-grained output with namespace and attribute support.

What You Will Learn

  • How to convert data models to/from XML
  • How to parse XML from streams and files
  • How to write XML output, including headers and namespace declarations
  • How to use XML-specific meta-properties: XML Attribute and Namespace URI
  • How to process large XML files with streaming cursors
  • How to build XML output incrementally and append entries

When To Use This

Use Martini XML functions when you need to:

  • Convert data models to XML for API responses, file exports, or messaging systems that require XML format
  • Parse incoming XML payloads from HTTP requests, message queues, or file imports
  • Handle XML attributes and namespaces in structured documents for enterprise or standards-compliant integrations
  • Process large XML files that cannot fit comfortably in memory using cursors
  • Write structured XML output incrementally during a service or workflow execution

Prerequisites

XML Conversion Functions

Martini provides a set of functions for converting between data models and XML representations. These cover the most common data exchange scenarios: serializing models to strings, deserializing strings and streams back to models, and writing XML to Java output targets.

Getting Started with XML Data Model Conversion

The quickest way to produce an XML string from a data model is modelPropertyToXmlString. Given a data model with populated properties, the function serializes every property into a valid XML document that you can use immediately.

Example — Converting a data model to an XML string:

Suppose you have a data model productData with these properties set at runtime:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
productData {
    status = success
    data {
        id = 101
        title = Sample Product
        price = 19.99
        inStock = true
    }
    message = Product fetched successfully
}

Call the function with modelProperty set to productData:

1
modelPropertyToXmlString(modelProperty: productData)

Expected result: The function returns the following XML string:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<?xml version="1.0"?>
<productData>
    <status>success</status>
    <data>
        <id>101</id>
        <title>Sample Product</title>
        <price>19.99</price>
        <inStock>true</inStock>
    </data>
    <message>Product fetched successfully</message>
</productData>

Example — Parsing an XML string back into a data model:

To reverse the operation, use xmlStringToModelProperty, then map its return value to a typed data model:

1
2
xmlStringToModelProperty(xmlString: myXmlString)
// Map the returned object to a typed data model whose property names match the XML element names

The xmlStringToModelProperty function returns a wrapped object rather than a typed data model. Map this object to a target data model whose property names match the XML element names to access the parsed values.

Expected result: After mapping, the target data model contains the properties from the XML string with their values correctly populated.

XML Conversion Function Reference

The following table lists the available XML conversion functions and their primary uses:

Function Primary Use
modelPropertyToXmlString(...) Convert a data model into an XML string
xmlStringToModelProperty(...) Parse an XML string into a wrapped data model object
inputStreamToModelProperty(...) Convert XML content from an InputStream into a wrapped data model object
readerToModelProperty(...) Convert XML content from a Reader into a wrapped data model object
writeXml(...) Stream XML output directly to a Writer

How XML Type Mapping Works

Understanding how Martini maps data between data models and XML helps you predict and control the output of conversion functions in both directions.

Data model to XML — type mapping rules:

XML does not have native data types. All property values are serialized as element text content, and nested data models become nested elements:

Data model property type XML representation
Any scalar value Text content inside an element (<tag>value</tag>)
Nested data model Nested XML element
Array property Repeated sibling XML elements

Property names become element names, and property values become element text content.

Null properties are excluded by default

When serializing a data model to XML, properties with null values are omitted unless the includeNulls parameter is set to true (where supported) or the property is marked as Required. A property marked as Required is always serialized, even when its value is null. For details on configuring the Required meta-property, see Accessing Individual Property Configuration.

XML Attribute meta-property:

A property marked with the XML Attribute meta-property is serialized as an attribute on its parent element—not as a child element. For details on configuring this meta-property, see Accessing Individual Property Configuration.

For example, if id is a child of data and has XML Attribute checked:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<!-- Without XML Attribute: 'id' renders as a child element -->
<data>
    <id>101</id>
    <title>Sample Product</title>
</data>

<!-- With XML Attribute on 'id': 'id' renders as an attribute on the parent element -->
<data id="101">
    <title>Sample Product</title>
</data>

XML Attribute takes precedence over Namespace URI

If a property has both a Namespace URI set and the XML Attribute meta-property checked, it is treated as an XML attribute and the Namespace URI is disregarded for that property.

Namespace URI meta-property:

A property with the Namespace URI meta-property set includes the namespace declaration on its corresponding XML element. For details on configuring this meta-property, see Accessing Individual Property Configuration.

For example:

1
2
3
<data xmlns="http://example.com/schema">
    <id>101</id>
</data>

If a parent element and one of its child elements share the same Namespace URI, the child does not repeat the namespace declaration—the parent's declaration already covers it:

1
2
3
4
<!-- 'data' declares the namespace; 'id' with the same URI inherits it silently -->
<data xmlns="http://example.com/schema">
    <id>101</id>
</data>

XML declaration header (insertHeader):

The insertHeader parameter (defaults to true) controls whether the XML declaration is prepended to the output:

1
<?xml version="1.0"?>

Set insertHeader to false to omit this header if the consuming system does not expect it.

Namespace prefix declarations (declaredNamespaces):

The declaredNamespaces parameter accepts a map of namespace prefixes to namespace URIs:

1
2
3
4
modelPropertyToXmlString(
    modelProperty: myModel,
    declaredNamespaces: [myPrefix: 'http://example.com/schema']
)

This causes the specified prefix to be used wherever that namespace URI appears in the output. The typical use case is when the namespace URI is already declared somewhere in the document—this parameter provides the prefix mapping so the prefix is used consistently rather than the full URI being inlined on every element.

XML to data model — type mapping rules:

When converting XML back to a data model, all property types are String regardless of the original value content:

XML structure Data model representation
Element with text content String property with the text value
Nested element Nested data model
Element attribute Nested String property (alongside other child properties)
Element with namespace URI Property with the Namespace URI meta-property set to that URI value

XML Conversion Benefits and Use Cases

XML conversion functions eliminate manual parsing and serialization code, reducing development time and the risk of producing malformed output. Common real-world applications include:

  • Enterprise integration: Parse incoming SOAP or XML-based API payloads using inputStreamToModelProperty, process them as data models, then serialize the response with modelPropertyToXmlString.
  • Standards-compliant output: Use the Namespace URI meta-property and declaredNamespaces to produce XML documents that conform to industry schemas such as XBRL, UBL, or custom XML standards.
  • Attribute-driven formats: Mark selected properties with the XML Attribute meta-property to generate XML where identifiers or metadata appear as element attributes rather than child elements.

Troubleshooting XML Conversion Issues

Problem Detection Cause Fix
Properties missing from generated XML output Expected elements do not appear in the XML output Properties with null values are excluded from serialization by default Set includeNulls to true on functions that support it (such as writeXml) to serialize all null properties. To serialize only specific null properties, mark them as Required.
A property appears as an attribute instead of a child element Output XML has an unexpected attribute on the parent element The property has the XML Attribute meta-property checked Uncheck XML Attribute to serialize the property as a child element instead
Child element repeats the parent's namespace declaration Namespace URI appears on both parent and child elements The child has a different Namespace URI value set; the parent's declaration does not cover it Set the same Namespace URI on the child as the parent to have the parent's declaration cover it
All parsed values are strings after XML-to-model conversion Numeric or boolean fields are typed as String after parsing XML does not encode type information; all values parse as String Cast string values to the required type in subsequent steps, or map parsed strings to a typed data model with explicitly typed properties

XML Streaming Cursor Functions

Martini's XML cursor functions let you read and write large XML datasets one element at a time, avoiding the memory overhead of loading an entire document into memory at once. For a general introduction to cursors in Martini, see Martini Cursors.

Getting Started with XML Input Cursors

Use openXmlFileInputCursor to iterate over specific elements in an XML file without loading the full file into memory.

Example workflow structure using openXmlFileInputCursor:

flowchart LR
    s[...]
    a["`**Function Node:** Open XML file input cursor and map to _myCursor_`"]
    b["`**Repeat Node:** Iterate over _myCursor_`"]
    c["`Process the current _myCursor_ entry`"]
    d[...]
    s --> a --> b -- each --> c
    b -- then --> d

Map the input cursor to an array property (here, myCursor), then use a Repeat Node with myCursor as its input array. Each iteration yields one matched XML element.

Expected result: Each iteration provides a nodeName identifying which XML element was matched and a cursorRecord containing the parsed value for that element.

How XML Input Cursor Streaming Works

Martini provides two functions for opening XML input cursors:

  • openXmlFileInputCursor(...) — Opens a cursor on an XML file at a specified file path.
  • openXmlStreamInputCursor(...) — Opens a cursor on an XML InputStream.

Both functions accept an xmlNodeNames parameter—a list of XML element names to seek while scanning the document. The cursor scans the XML sequentially and yields only elements matching those names.

How each cursor iteration works:

Each iteration of the cursor returns a data model entry with two properties:

  • nodeName — The matched XML element name from xmlNodeNames
  • cursorRecord — The content of that element (a string value or a nested data model)

Example — Iterating over selected elements:

Given this XML input:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<?xml version="1.0"?>
<response>
    <status>success</status>
    <data>
        <id>101</id>
        <title>Sample Product</title>
        <price>19.99</price>
        <inStock>true</inStock>
    </data>
    <message>Product fetched successfully</message>
</response>

With xmlNodeNames set to ["status", "data"]:

  • Iteration 1: nodeName = "status", cursorRecord = "success"
  • Iteration 2: nodeName = "data", cursorRecord contains a data model with properties id, title, price, and inStock
  • The cursor ends—"message" is not in xmlNodeNames and is skipped.

Targeting nested elements directly:

Element names can reference nested elements by name. For example, specifying "inStock" instead of "data" yields:

  • nodeName = "inStock", cursorRecord = "true"

Parent and Child Element Matching

If xmlNodeNames includes both a parent element (for example, "data") and a child of that element (for example, "inStock"), the cursor returns the parent's record and does not separately yield the child. To retrieve only the child, remove the parent name from xmlNodeNames.

How XML Output Cursors Work

The openXmlOutputCursor(...) function opens a write cursor that incrementally writes XML elements to a configured destination, such as a StringBuffer or OutputStream. The function also returns a Writer you can use to write content—such as the XML declaration and root element tags—around the cursor-written elements.

This approach is especially useful when the XML root wraps an array of elements and you want to avoid loading all entries into memory at once.

Example workflow structure — Writing an XML document with an array root:

flowchart TD
    s[...]
    a["`Prepare _products_ data model array`"]
    b["`**Function Node:** Open XML output cursor; map cursor to _myOutputCursor_, writer to _myWriter_`"]
    c["`**Script Node:** Write XML declaration and opening root element using _myWriter_`"]
    d["`**Repeat Node:** Iterate over _products_ with _myOutputCursor_ as the output array`"]
    e["`**Map Step:** Map the current _products_ entry to the current _myOutputCursor_`"]
    f["`**Script Node:** Write closing root element and close _myWriter_`"]
    g[...]
    s --> a --> b --> c --> d -- each --> e
    d -- then --> f --> g

Assuming you are writing to /output/products.xml, start with a products data model array already populated with entries—in this case two entries:

  • id: 1, title: "Sample Product 1"
  • id: 2, title: "Sample Product 2"

Open the output cursor using openXmlOutputCursor with these parameters:

  • insertHeader: false — The XML declaration is written manually, so the cursor should not prepend it
  • elementName: "product" — Each appended entry is wrapped in a <product> element
  • closeDestination: false — The cursor should not close the underlying writer when it finishes, so you can still write the closing root tag afterward

Map the returned cursor array property to myOutputCursor and the returned Writer to myWriter.

In the first Script Node, write the XML declaration and the opening root element tag:

1
myWriter.write('<?xml version="1.0"?>\n<products>\n')

In the Repeat Node, products is set as the input array and myOutputCursor as the output array. For each iteration, the current products entry is mapped to the current myOutputCursor—this appends the entry to the cursor and writes it to the destination as a <product> element.

After the Repeat Node completes, a second Script Node writes the closing root element tag and closes the writer:

1
2
myWriter.write('</products>')
myWriter.close()

Expected result: /output/products.xml contains a valid XML document:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<?xml version="1.0"?>
<products>
<product>
    <id>1</id>
    <title>Sample Product 1</title>
</product>
<product>
    <id>2</id>
    <title>Sample Product 2</title>
</product>
</products>

Writing individual XML elements (non-array output):

Output cursors are not limited to repeated elements. To write individual fields, append a data model with the desired properties directly to the cursor—without a Repeat Node or an Iterate Step. For example, appending a model with status = "success" produces <status>success</status> in the XML output.

To append data models to a cursor outside of a Repeat node or Iterate step, see Service Functions for cursor interaction functions.

You can also change the element name used by an output cursor at runtime by calling setCursorElementName(...), which updates how all subsequently appended models are wrapped. This allows the same cursor to emit different element names dynamically.

XML Streaming Benefits and Use Cases

Cursor-based streaming is the right choice when working with XML data at scale. Instead of loading an entire document into memory, your service processes one element at a time—keeping memory usage constant regardless of file size.

Real-world applications include:

  • Large file processing: Stream a multi-gigabyte product catalog XML file and transform each product element individually.
  • ETL pipelines: Read from an XML source stream, apply transformations per element, and write to an XML output file—all within a single service.
  • Selective extraction: Extract only specific named elements from a complex XML document without parsing the entire structure.

Troubleshooting XML Cursor Issues

Problem Detection Cause Fix
Fewer cursor records than expected Iteration ends before all matching elements are processed A specified element name is a child of another specified element name; the cursor returns the parent's record instead of separately yielding the child Remove the parent element name from xmlNodeNames and keep only the child, or vice versa
Output XML is invalid after writing The final XML file fails validation or cannot be parsed Output cursor configuration causes malformed nesting or missing root element Review and correct the configuration on openXmlOutputCursor to ensure proper XML element nesting

Helpful Resources