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
- Martini Designer installed and running
- A Martini package in the current workspace
- A service or workflow to invoke the functions from
- Basic understanding of the following:
- Invoke Function Node for workflows
- Function Step for services
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 | |
Call the function with modelProperty set to productData:
1 | |
Expected result: The function returns the following XML string:
1 2 3 4 5 6 7 8 9 10 11 | |
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 | |
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 | |
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 | |
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 | |
XML declaration header (insertHeader):
The insertHeader parameter (defaults to true) controls whether the XML declaration is prepended to the output:
1 | |
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 | |
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 withmodelPropertyToXmlString. - Standards-compliant output: Use the Namespace URI meta-property and
declaredNamespacesto 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 fromxmlNodeNamescursorRecord— 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 | |
With xmlNodeNames set to ["status", "data"]:
- Iteration 1:
nodeName = "status",cursorRecord = "success" - Iteration 2:
nodeName = "data",cursorRecordcontains a data model with propertiesid,title,price, andinStock - The cursor ends—
"message"is not inxmlNodeNamesand 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 itelementName: "product"— Each appended entry is wrapped in a<product>elementcloseDestination: 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 | |
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 | |
Expected result: /output/products.xml contains a valid XML document:
1 2 3 4 5 6 7 8 9 10 11 | |
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
- Data Models — Overview of Martini data models and how they represent structured data
- Data Model Cursors — Detailed reference for working with cursors in Martini
- Iterate Step — How to use the Iterate step to process arrays and cursors in services
- Function Step — How to invoke built-in functions from a service
- Invoke Function Node — How to invoke functions from a workflow
- Service Functions — Additional functions for interacting with cursors
- Community Q&A: Martini Community
Have a question? Post or search it here.