Martini JSON Functions
Overview
Martini's built-in JSON functions let you convert, read, and write JSON data directly inside your services and workflows—without external libraries or boilerplate code. Use these functions to transform data models to JSON strings, parse JSON from files and streams, and stream large JSON datasets one record at a time to keep memory usage low.
What You Will Learn
- How to convert models to/from JSON
- How to parse JSON from streams and files
- How to write and format JSON output
- How to process large JSON with streaming cursors
- How to build JSON incrementally and append entries
When To Use This
Use Martini JSON functions when you need to:
- Convert data models to JSON for API responses, file exports, or message publishing
- Parse incoming JSON payloads from HTTP requests, message queues, or file imports
- Process large JSON files that cannot fit comfortably in memory using cursors
- Write structured JSON 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
JSON Conversion Functions
Martini provides a set of functions for converting between data models and JSON representations. These cover the most common data exchange scenarios: serializing models to strings, deserializing strings and streams back to models, and writing JSON to Java output targets.
Getting Started with JSON Data Model Conversion
The quickest way to produce a JSON string from a data model is to use modelPropertyToJsonString.
Given a data model with populated properties, the function serializes every property into a valid JSON string you can use immediately.
Example — Converting a data model to a JSON string:
Suppose you have a data model myDataModel with these properties set at runtime:
1 2 3 4 5 6 7 8 9 10 | |
Call the function with modelProperty set to myDataModel:
1 | |
Expected result: The function returns the following JSON string:
1 2 3 4 5 6 7 8 9 10 | |
Example — Parsing a JSON string back into a data model:
To reverse the operation, use jsonStringToModelProperty and then map its return value to a typed data model:
1 2 | |
The jsonStringToModelProperty function returns a wrapped object rather than a typed data model.
Map the returned object to a target data model whose property names match the JSON keys to access the parsed values.
Expected result: After mapping, the target data model contains the properties from the JSON string with their values correctly populated.
JSON Conversion Function Reference
The following table lists the available JSON conversion functions and their primary uses:
| Function | Primary Use |
|---|---|
modelPropertyToJsonString(...) |
Convert a data model into a JSON string |
jsonStringToModelProperty(...) |
Parse a JSON string into a wrapped data model object |
inputStreamToModelProperty(...) |
Convert JSON content from an InputStream into a wrapped data model object |
readerToModelProperty(...) |
Convert JSON content from a Reader into a wrapped data model object |
writeJson(...) |
Stream JSON output directly to a Writer |
printJson(...) |
Generate formatted, human-readable JSON output using an IndentPrinter |
How JSON Type Mapping Works
Understanding how Martini maps types between data models and JSON helps you predict and control the output of conversion functions in both directions.
Data model to JSON — type mapping rules:
Each data model property's value type determines the corresponding JSON value type:
| Data model property type | JSON value type |
|---|---|
String |
JSON string ("value") |
Whole number (Integer, Long) |
JSON number (101) |
Decimal number (Float, Double, BigDecimal) |
JSON number (19.99) |
Boolean |
JSON boolean (true / false) |
| Nested data model | JSON object ({ ... }) |
| Array property | JSON array ([ ... ]) |
Null properties are excluded by default
When serializing a data model to JSON, properties with null values are omitted unless the includeNulls parameter is set to true or the property is marked as Required.
Required properties are always serialized, even when their value is null.
For details on configuring the Required meta-property, see Accessing Individual Property Configuration.
JSON to data model — type mapping rules:
When converting JSON back to a data model, the resulting property types are inferred from the JSON value:
| JSON value type | Data model property type |
|---|---|
| JSON string | String |
| JSON number (integer or decimal) | BigDecimal |
| JSON boolean | Boolean |
| JSON object | Nested data model |
| JSON array | Array property; element type inferred from the first element in the array |
| JSON null field | Property of type Object with a null value |
JSON Conversion Benefits and Use Cases
JSON conversion functions eliminate manual parsing and serialization code, reducing development time and the risk of producing malformed output. Common real-world applications include:
- API integration: Parse an incoming JSON request body from an
InputStreamusinginputStreamToModelProperty, process it, then serialize the result withmodelPropertyToJsonStringfor the API response. - File export: Populate a data model with records and write a formatted JSON file using
printJsonwith anIndentPrinter. - Message processing: Deserialize JSON messages from a queue into typed data models for safe downstream processing.
Troubleshooting JSON Conversion Issues
| Problem | Detection | Cause | Fix |
|---|---|---|---|
| Some properties are missing from the generated JSON output | Expected fields do not appear in the JSON output | Properties with null values are excluded by default during serialization |
Set includeNulls to true to serialize all null properties. To serialize only specific null properties, mark them as Required in the data model. |
| Array element types are incorrect after deserialization | Array items are treated as unexpected types | Array element type inference is based on the first array element | Ensure arrays contain consistent element types before conversion |
| JSON parsing fails during conversion | Conversion function throws a parse or syntax exception | JSON input is malformed or contains invalid syntax | Validate the JSON structure and confirm the payload is valid JSON before conversion |
JSON Streaming Cursor Functions
Martini's JSON cursor functions let you read and write large JSON datasets one record at a time, avoiding the memory overhead of loading an entire file or stream into memory at once. For a general introduction to cursors in Martini, see Martini Cursors.
Getting Started with JSON Input Cursors
Use openJsonFileInputCursor to iterate over specific nodes in a JSON file without loading the full file into memory.
Example workflow structure using openJsonFileInputCursor:
flowchart LR
s[...]
a["`**Function Node:** Open JSON 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 JSON node.
Expected result: Each iteration provides a nodeName identifying which JSON field was matched and a cursorRecord containing the parsed value for that node.
How JSON Input Cursor Streaming Works
Martini provides two functions for opening JSON input cursors:
openJsonFileInputCursor(...)— Opens a cursor on a JSON file at a specified file path.openJsonStreamingInputCursor(...)— Opens a cursor on a JSON InputStream.
Both functions accept a jsonNodeNames parameter—a list of JSON field names to seek while scanning the data.
The cursor scans the JSON sequentially and yields only records matching those names.
How each cursor iteration works:
Each iteration of the cursor returns a data model entry with two properties:
nodeName— The matched JSON field name fromjsonNodeNamescursorRecord— The content of that node (a scalar value or a data model)
Example — Iterating over selected nodes:
Given this JSON input:
1 2 3 4 5 6 7 8 9 10 | |
With jsonNodeNames 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 injsonNodeNamesand is skipped.
Targeting a root-level JSON array:
If the JSON root is an array, add the special value $jsonRoot to jsonNodeNames:
1 | |
Each cursor record corresponds to one entry in the root array.
For example, if the file contains an array of three objects, the cursor yields three iterations—one cursorRecord per array entry.
Targeting nested fields directly:
Node names can reference nested fields by name. For example, specifying "inStock" instead of "data" yields:
nodeName = "inStock",cursorRecord = true
Parent and Child Node Matching
If jsonNodeNames includes both a parent node (for example, "data") and a child of that node (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 jsonNodeNames.
How JSON Output Cursors Work
The openJsonOutputCursor(...) function opens a write cursor that incrementally builds a JSON document and writes it to a configured destination, including (but not limited to) a StringBuffer or OutputStream.
Once the cursor is open, append data models to it. Each appended model contributes its properties as JSON fields or array entries in the final output.
Example workflow structure — Writing a JSON array:
flowchart LR
s[...]
a["`Prepare _products_ data model array`"]
b["`**Function Node:** Open JSON output cursor and map to _myOutputCursor_`"]
c["`**Repeat Node:** Iterate over _products_ with _myOutputCursor_ as the output array`"]
d["`**Map Step**: Map the current _products_ to the current _myOutputCursor_`"]
e[...]
s --> a --> b --> c -- each --> d
c -- then --> e
Assuming you are writing to /output/products.json, start with a products data model array already populated with entries you want to write—in this case two entries:
id: 1, title: "Sample Product 1"id: 2, title: "Sample Product 2"
The output cursor returned by the function is then mapped to myOutputCursor.
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 is what appends the entry to the cursor and writes it to the destination as a JSON array entry.
Expected result: /output/products.json contains a JSON array with two entries:
1 2 3 4 | |
Writing individual JSON fields (non-array output):
Output cursors are not limited to arrays.
To write individual fields, append a data model with the desired properties directly to the cursor—without an Repeat Node or an Iterate Step.
For example, appending a model with status = "success" produces "status": "success" in the JSON output.
To append data models to a cursor outside of an Repeat node or Iterate step, see Service Functions for cursor interaction functions.
JSON Streaming Benefits and Use Cases
Cursor-based streaming is the right choice when working with JSON data at scale. Instead of loading an entire dataset into memory, your service processes one record at a time—keeping memory usage constant regardless of file size.
Real-world applications include:
- Large file processing: Stream a multi-gigabyte product catalog JSON file and transform each product record individually.
- ETL pipelines: Read from a JSON source stream, apply transformations per record, and write to a JSON output file—all within a single service.
- Selective extraction: Extract only specific named nodes from a complex JSON document without parsing the entire structure.
Troubleshooting JSON Cursor Issues
| Problem | Detection | Cause | Fix |
|---|---|---|---|
| Fewer cursor records than expected | Iteration ends before all matching nodes are processed | A specified node name is a child of another specified node name; the cursor returns the parent's record instead of separately yielding the child | Remove the parent node name from jsonNodeNames and keep only the child, or vice versa |
| Root-level array yields no records | Cursor returns zero iterations on a JSON file whose root is an array | The root array has no field name and $jsonRoot was not included in jsonNodeNames |
Add "$jsonRoot" to jsonNodeNames to target the unnamed root array |
| Output JSON is invalid after writing | The final JSON file fails validation or cannot be parsed | Print configuration causes { and } to wrap what should be a root-level JSON array |
Review and correct the print configuration on openJsonOutputCursor to match the intended JSON structure |
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.