Skip to content

Martini Data Model Object Converters

Overview

Martini automatically converts objects between different types during service and workflow execution. This eliminates manual type casting and enables flexible data mapping across your services or workflows.

What You Will Learn

  • How Martini's automatic type conversion works
  • Which built-in converters are available and what transformations they support
  • How to create and register custom converters for specialized data transformations

When To Use This

Use this guide to:

  • Learn how Martini's built-in converters work behind the scenes during data mapping operations
  • Choose appropriate property types knowing which conversions are supported automatically
  • Create custom converters when you need specialized transformations that aren't covered by built-in converters

Prerequisites

How Martini Object Conversion Works

Martini automatically handles type conversions through the following steps:

  1. Type Compatibility Check: Determines if the source and target types require conversion
  2. Converter Discovery: Iterates through registered converters to find one that supports the required transformation
  3. Automatic Conversion: Performs the conversion using the appropriate converter
  4. Value Assignment: Assigns the converted value to the target property

This process operates transparently, automatically handling conversions during data mapping or assigning property values.

Built-in Converter Reference

Martini includes converters for most common data transformations, with each one specializing in a specific type conversion.

AssignableFromConverter

The foundation converter that handles direct type assignments and inheritance relationships.

Supported Conversions: Same type assignments and superclass/interface relationships

This converter performs the Java equivalent of:

1
2
if (targetClass.isAssignableFrom(sourceValue.getClass()))
  return sourceValue;

Key Benefits: Zero-overhead conversion when no transformation is needed, preserving object identity and performance.

NumberConverter

Handles all numeric type conversions including primitives, wrapper classes, and string representations.

Supported Source Types:

  • CharSequence objects
  • Primitive numbers (int, short, float, double, long, byte, char)
  • Number wrapper classes

Supported Target Types:

Character Conversion Behavior: When converting to char or Character, the converter calls toString() on the source and uses the first character as the value. Returns 0 if the string is null or empty.

IterableConverter

Converts between different collection types and handles single-value to collection transformations.

Supported Source Types:

Supported Target Types:

Single Property Handling: When the source is a non-array property, the target collection contains one entry with the property's value.

ArrayConverter

Leverages all other built-in converters for element transformations.

Core Capabilities:

  • Java object arrays ↔ Collections
  • Individual element type conversion using other converters
  • Property creation from compatible arrays
  • Byte array to String conversion for binary data handling

Advanced Features: This converter recursively applies other converters to handle complex nested transformations, such as converting an array of strings to a list of integers.

GloopObjectMapConverter

Bridges Martini data models with standard Java Map objects for seamless data structure interoperability.

Supported Conversions:

Use Cases: Essential for API integrations where external systems expect Map structures but your services use data models.

CharSequenceToClassConverter

Enables runtime class instantiation from string class names.

Functionality: Converts CharSequence values to Class objects by treating the string as a fully qualified class name.

Security Considerations: Use with caution in production environments as it can instantiate arbitrary classes.

CharSequenceToClosureConverter

Compiles string representations into executable Groovy closures for dynamic script execution.

Functionality: Converts CharSequence to Groovy closures by compiling the string as Groovy code.

Use Cases: Dynamic workflow logic, configurable business rules, and runtime script execution.

CharSequenceToEnumConverter

Converts string values to corresponding enumeration constants.

Functionality: Maps CharSequence values to enum constants by matching string values to enum names.

Error Handling: Throws exceptions for invalid enum values, ensuring type safety in enum conversions.

CharSequenceToRunnableConverter

Compiles string code into executable Java Runnable objects for task execution.

Functionality: Converts CharSequence to Runnable by compiling the string as executable Java code.

Applications: Dynamic task creation, configurable background processes, and runtime job definition.

DateCalendarConverter

Comprehensive date and time conversion supporting multiple input formats and output types.

Supported Input Types:

Supported Output Types:

Supported Date Format Recognition

The converter automatically recognizes these string formats:

  1. ISO 8601 Formats:

    • YYYY-MM-dd'T'HH:mm:ss'Z' (example: 1994-11-05T08:15:30Z)
    • YYYY-MM-dd'T'HH:mm:ssZ (example: 1994-11-05T08:15:30-05:00)
    • YYYY-MM-dd'T'HH:mm:ss.SSSZ (example: 2013-12-14T01:55:33.412Z)
  2. Locale-Specific Formats:

    • EEE MMM dd HH:mm:ss zzz yyyy (example: Sun Dec 30 00:00:00 PHT 2007)
    • yyyy-MM-dd hh:mm:ss.S (example: 2007-12-30 00:00:00.0)
  3. Special Values:

    • "now" - Uses current system time
    • Numeric strings - Interpreted as Unix timestamps
  4. XML Date/Time Formats - Standard XML Schema date, time, and datetime representations

Custom Date Format Configuration

For date formats not recognized by the built-in converter, configure custom patterns using the following property meta-properties:

  • Date Formats: Custom input format patterns
  • Output Expression: Custom output format for data serialization

Custom formats are processed before the built-in DateCalendarConverter, giving you control over specialized date handling.

ObjectToBooleanConverter

Converts various object types to boolean values using intuitive logic rules.

Conversion Rules:

  • Number objects: true if value > 0, false otherwise
  • CharSequence objects: true if toString() equals "true" (case-insensitive)

ObjectToConstructorConverter

Enables automatic object instantiation when target classes have compatible single-argument constructors.

Functionality: Searches target classes for constructors that accept the source object type as a parameter, then uses reflection to create instances.

Use Cases: Converting between wrapper types, creating domain objects from primitive values, and enabling custom object initialization patterns.

CharSequenceToFileConverter

Creates File objects from string file paths.

Functionality: Converts CharSequence values to File objects using the string as the file path.

Path Handling: Supports both absolute and relative file paths, following standard Java File constructor behavior.

InputStreamConverter

Converts various input data sources to standard Java input streams for consistent data reading.

Supported Source Types:

Supported Target Types:

OutputStreamConverter

Converts various output destinations to standard Java output streams for consistent data writing.

Supported Source Types:

Supported Target Types:

StreamToContentConverter

Reads complete data content from various input sources into memory for processing.

Similar to InputStreamConverter but reads all available data into target objects rather than providing stream access.

Supported Target Types:

Use Cases: Loading complete file contents, processing uploaded data, and converting streams to strings for text processing.

ContentToStreamConverter

Converts diverse content sources into standard Java I/O objects, including web-specific types.

Supported Source Types:

Supported Target Types:

Web Integration Benefits: Seamlessly handles HTTP file uploads in Martini, automatically converting uploaded files to usable I/O objects.

ObjectToCharSequenceConverter

Converts various data types to text representations for display, logging, or further text processing.

Supported Source Types:

Supported Target Types:

Adding Custom Converters

Create specialized converters for data transformations not covered by Martini's built-in converters. Custom converters integrate seamlessly with Martini's automatic conversion system, enabling complex business logic and domain-specific transformations.

Custom Converter Interface Requirements

All custom converters must implement the GloopObjectConverter interface, which provides two essential methods for conversion logic.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
package io.toro.gloop.object.converter;

interface GloopObjectConverter {

    /**
     * @return {@code true} if the converter supports the conversion
     */
    boolean supports(Class from, Class to);


    /**
     * Perform the actual conversion.
     * @throws ClassCastException if it cannot do the conversion.
     */
    Object convertTo(Object from, Class to);

}

Method Implementation Guidelines:

  • supports(): Return true only for conversions your converter can reliably handle
  • convertTo(): Always return an object of the target type or throw ClassCastException
  • Error Handling: Use descriptive exception messages to aid debugging

Creating a Custom Converter Implementation

This example demonstrates the structure and approach for converting between custom POJOs and data models. The implementation details are left as placeholder comments to focus on the converter interface and registration pattern.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import io.toro.gloop.object.converter.GloopObjectConverter;
import io.toro.gloop.object.converter.GloopObjectConverterManager;
import io.toro.gloop.object.property.GloopModel;

public class CustomPojoConverter implements GloopObjectConverter {

    @Override
    public boolean supports(Class from, Class to) {
        // POJO to Model conversion
        if (to.isAssignableFrom(GloopModel.class) && MyCustomPojo.class.isAssignableFrom(from)) {
            return true;
        }
        // Model to POJO conversion
        if (to.isAssignableFrom(MyCustomPojo.class) && GloopModel.class.isAssignableFrom(from)) {
            return true;
        }
        return false;
    }

    @Override
    public Object convertTo(Object from, Class to) {
        if (from instanceof MyCustomPojo) {
            // Convert custom POJO to Model...
            return model;
        }

        if (from instanceof GloopModel) {
            // Convert Model to custom POJO...
            return pojo;
        }

        throw new ClassCastException(
            String.format("Cannot convert from %s to %s", 
                    from.getClass().getSimpleName(), 
                    to.getSimpleName())
        );
    }

    /**
     * Register this converter with Martini
     * Configure this method as a startup handler in your package
     */
    public static void register() {
        GloopObjectConverterManager.registerConverter(new CustomPojoConverter());
    }
}

Registering Custom Converters

Register your converter during package startup to ensure it's available throughout your application's lifecycle.

Configure your converter register() method as a startup handler in your package configuration.

Converter Priority Management

Control converter execution order using the priority parameter in registerConverter():

1
2
3
4
5
// Register with specific priority (lower numbers = higher priority)
GloopObjectConverterManager.registerConverter(new CustomPojoConverter(), 0);

// Register at the end of the converter chain (default behavior)
GloopObjectConverterManager.registerConverter(new CustomPojoConverter());

Using Custom Converters

Once registered, your converter works automatically during data mapping and property assignment operations in services and workflows. For example, when mapping your custom POJO to a data model property or vice versa, Martini will automatically detect and use your converter to handle the transformation.

Troubleshooting Custom Converter Issues

Problem Detection Cause Fix
Converter not called Conversion fails with standard converters Registration failed or incorrect supports() logic Verify startup service configuration and check supports() method conditions
ClassCastException during conversion Runtime exception in convertTo() Invalid return type or missing null checks Ensure method returns correct type and handle edge cases
Wrong converter priority Expected converter not used first Priority value too high Lower priority number for higher precedence

Helpful Resources