Skip to main content

Key Benefits

  • Rich Library of Transformations: Over 70 built-in transformers covering common use cases
  • Composability: Chain multiple transformations together
  • Type Safety: Transformers handle different data types intelligently
  • Extensibility: Create custom transformers for your specific needs
  • Clean Syntax: Fluent DSL for readable mapping definitions

Getting Started

Basic Transformation Syntax

Transformations are applied between the map() and to() methods in a mapping:
Platymap.flow("source")
    .to("target")
    .map("name").uppercase().to("fullName").end()
    .build()
In this example, the uppercase() transformation converts the value of name to uppercase before mapping it to fullName.

How Transformations Work

Each transformation is a function that:
  1. Takes an input value
  2. Processes it according to specific rules
  3. Returns a transformed value
Transformations can be chained, with each one receiving the output of the previous transformation:
.map("text").trim().uppercase().to("processedText").end()
This first trims whitespace from text, then converts the result to uppercase.

String Transformations

String transformations modify text values. These are especially useful for data cleaning, formatting, and normalization.

Text Case Transformations

TransformationDescriptionExample InputExample Output
uppercase()Converts text to uppercase”hello world""HELLO WORLD”
lowercase()Converts text to lowercase”Hello World""hello world”
capitalize()Capitalizes first letter of each word”hello world""Hello World”
titleCase()Converts to title case”hello world""Hello World”
camelCase()Converts to camelCase”hello world""helloWorld”
snakeCase()Converts to snake_case”Hello World""hello_world”
kebabCase()Converts to kebab-case”Hello World""hello-world”

Text Formatting Transformations

TransformationDescriptionExample InputExample Output
trim()Removes whitespace from start and end” hello ""hello”
truncate()Truncates to 30 chars with ellipsis”This is a very long text that needs truncating""This is a very long text that…”
truncateLong()Truncates to 100 chars with ellipsisLong text…Truncated long text…
stripHtml()Removes HTML tags”<p>Hello</p>""Hello”
mask()Masks all but last 4 chars”1234567890""******7890”
spacesToDashes()Replaces spaces with dashes”hello world""hello-world”
newlinesToBreaks()Replaces newlines with <br>“line1\nline2""line1<br>line2”
slugify()Creates a URL-friendly slug”Hello World! 123""hello-world-123”

Text Padding Transformations

TransformationDescriptionExample InputExample Output
padLeft10()Pads left to 10 chars with zeros”123""0000000123”
padRight20()Pads right to 20 chars with spaces”hello""hello “

Example Usage

val mapping = Platymap.flow("product")
    .to("displayProduct")
    .map("name").trim().capitalize().to("displayName").end()
    .map("description").stripHtml().truncate().to("shortDescription").end()
    .map("sku").padLeft10().to("formattedSku").end()
    .map("title").slugify().to("urlSlug").end()
    .build()

Number Transformations

Number transformations allow you to perform mathematical operations and format numeric values.

Mathematical Operations

TransformationDescriptionExample InputExample Output
increment()Adds 1 to a number56
decrement()Subtracts 1 from a number54
toPercentage()Multiplies by 1000.2525
fromPercentage()Divides by 100250.25
round()Rounds to nearest integer4.75
floor()Rounds down to nearest integer4.74
ceil()Rounds up to nearest integer4.25
abs()Takes absolute value-55
roundToTwoDecimals()Rounds to 2 decimal places4.76544.77
roundToWholeNumber()Rounds to a whole number4.75

Formatting Operations

TransformationDescriptionExample InputExample Output
formatNumber()Formats with commas and decimals1234.5”1,234.50”
formatUSD()Formats as USD1234.5”$1,234.50”
formatEUR()Formats as EUR1234.5”€1,234.50”
formatGBP()Formats as GBP1234.5”£1,234.50”

Example Usage

val mapping = Platymap.flow("product")
    .to("displayProduct")
    .map("price").roundToTwoDecimals().to("basePrice").end()
    .map("price").multiply(1.2).roundToTwoDecimals().to("retailPrice").end()
    .map("price").multiply(0.8).formatUSD().to("salePrice").end()
    .map("discount").toPercentage().round().to("discountPercent").end()
    .build()

Date Transformations

Date transformations help you format, manipulate, and extract information from dates.

Date Formatting

TransformationDescriptionExample InputExample Output
formatDateLong()Formats to long date format”2023-01-15""January 15, 2023”
formatDateShort()Formats to short date format”2023-01-15""01/15/2023”
formatDateTime()Formats date and time”2023-01-15T14:30:00Z""Jan 15, 2023 at 2:30 PM”
toIsoDate()Converts to ISO date format”01/15/2023""2023-01-15”
toIsoDateTime()Converts to ISO date-time format”01/15/2023""2023-01-15T00:00:00Z”
formatRelativeDate()Shows relative to current time”2023-01-15""2 months ago”

Date Manipulation

TransformationDescriptionExample InputExample Output
addOneDay()Adds one day to date”2023-01-15""2023-01-16”
addOneWeek()Adds one week to date”2023-01-15""2023-01-22”
addOneMonth()Adds one month to date”2023-01-15""2023-02-15”
addOneYear()Adds one year to date”2023-01-15""2024-01-15”

Date Extraction

TransformationDescriptionExample InputExample Output
extractYear()Extracts year from date”2023-01-15”2023
extractMonth()Extracts month from date”2023-01-15”1
extractDay()Extracts day from date”2023-01-15”15

Date Conversion

TransformationDescriptionExample InputExample Output
dateToTimestamp()Converts date to Unix timestamp”2023-01-15T00:00:00Z”1673740800
timestampToDate()Converts Unix timestamp to date1673740800”2023-01-15T00:00:00Z”

Example Usage

val mapping = Platymap.flow("order")
    .to("displayOrder")
    .map("orderDate").formatDateLong().to("formattedOrderDate").end()
    .map("orderDate").addOneWeek().formatDateShort().to("estimatedDelivery").end()
    .map("orderDate").extractYear().to("orderYear").end()
    .map("orderDate").formatRelativeDate().to("orderRelativeTime").end()
    .build()

Boolean Transformations

Boolean transformations convert true/false values to different formats and perform logical operations.

Boolean Conversion

TransformationDescriptionExample InputExample Output
booleanToString()Converts boolean to stringtrue”true”
toYesNo()Converts to “Yes” or “No”true”Yes”
toEnabledDisabled()Converts to “Enabled” or “Disabled”true”Enabled”
toActiveInactive()Converts to “Active” or “Inactive”true”Active”
booleanToNumber()Converts to 1 or 0true1
stringToBoolean()Converts string to boolean”yes”true

Logical Operations

TransformationDescriptionExample InputExample Output
negate()Reverses boolean valuetruefalse

Example Usage

val mapping = Platymap.flow("product")
    .to("displayProduct")
    .map("inStock").toYesNo().to("availability").end()
    .map("active").toActiveInactive().to("status").end()
    .map("featured").negate().to("nonFeatured").end()
    .map("status").stringToBoolean().to("isActive").end()
    .build()

Collection Transformations

Collection transformations work with arrays and lists of values.

Collection Operations

TransformationDescriptionExample InputExample Output
join()Joins array elements with spaces[“a”, “b”, “c”]“a b c”
joinComma()Joins array elements with commas[“a”, “b”, “c”]“a, b, c”
joinPipe()Joins array elements with pipes[“a”, “b”, “c”]“a | b | c”
size()Gets collection size[“a”, “b”, “c”]3
first()Gets first element[“a”, “b”, “c”]“a”
last()Gets last element[“a”, “b”, “c”]“c”
sort()Sorts elements[“c”, “a”, “b”][“a”, “b”, “c”]
reverse()Reverses order[“a”, “b”, “c”][“c”, “b”, “a”]
filterNulls()Removes null values[“a”, null, “c”][“a”, “c”]

String-to-Collection Conversion

TransformationDescriptionExample InputExample Output
split()Splits string on commas”a,b,c”[“a”, “b”, “c”]
splitComma()Splits string on commas”a, b, c”[“a”, “b”, “c”]

Example Usage

val mapping = Platymap.flow("product")
    .to("displayProduct")
    .map("tags").joinComma().to("tagList").end()
    .map("categories").size().to("categoryCount").end()
    .map("images").first().to("primaryImage").end()
    .map("tagString").split().to("tagArray").end()
    .build()

Conditional Transformations

Conditional transformations check conditions and provide default values.

Default Values

TransformationDescriptionExample InputExample Output
defaultIfNull()Uses default if value is nullnull""
defaultIfEmpty()Uses default if value is empty"""N/A”
ifNull()Replaces null with specified valuenull”null”
ifEmpty()Replaces empty with specified value"""empty”

Condition Checks

TransformationDescriptionExample InputExample Output
isNull()Checks if value is nullnulltrue
isEmpty()Checks if value is empty""true
isNumber()Checks if value is a number”123”true
equalsTest()Checks if equals “test""test”true
containsTest()Checks if contains “test""testing”true
startsWithPrefix()Checks if starts with “prefix""prefix123”true
endsWithSuffix()Checks if ends with “suffix""123suffix”true

Example Usage

val mapping = Platymap.flow("product")
    .to("displayProduct")
    .map("description").defaultIfNull("No description available").to("displayDescription").end()
    .map("brand").defaultIfEmpty("Generic").to("displayBrand").end()
    .map("sku").isEmpty().to("requiresSku").end()
    .map("price").isNumber().to("hasPricing").end()
    .build()

Type Conversion Transformations

Type conversion transformations change a value from one data type to another.

Basic Type Conversions

TransformationDescriptionExample InputExample Output
toString()Converts to string123”123”
toInt()Converts to integer”123”123
toDouble()Converts to double”123.45”123.45
toBoolean()Converts to boolean”true”true

Advanced Conversions

TransformationDescriptionExample InputExample Output
parseJson()Parses JSON string to object"{"a":1}"{a: 1}
toJsonString()Converts object to JSON string{a: 1}"{"a":1}"
toBase64()Encodes to Base64”hello""aGVsbG8=“
fromBase64()Decodes from Base64”aGVsbG8=""hello”
urlEncode()URL encodes a string”a b""a%20b”
urlDecode()URL decodes a string”a%20b""a b”

Example Usage

Here’s how to use these transformations in a mapping configuration:
val mapping = Platymap.flow("data")
    .to("processed")
    .map("numericId").toInt().to("id").end()
    .map("enabled").toBoolean().to("isActive").end()
    .map("metadata").toJsonString().to("metadataString").end()
    .map("rawData").fromBase64().to("decodedData").end()
    .build()

Common Use Cases

  • String to Number: Convert user input strings to numeric values for calculations
  • Boolean Conversion: Transform string flags (“true”/“false”) to boolean values
  • JSON Processing: Parse JSON responses or serialize objects for API calls
  • Encoding/Decoding: Handle Base64 data or URL-safe strings
  • Data Sanitization: Clean and format data before processing

Composing Transformations

One of the most powerful features of Platymap’s transformation system is the ability to compose multiple transformations together.

Basic Composition

Simply chain transformations in the order you want them applied:
.map("name").trim().uppercase().to("processedName").end()
This first trims whitespace, then converts to uppercase.

Common Composition Patterns

Some common composition patterns are available as convenience methods:
CompositionEquivalent To
trimAndUppercase()trim().uppercase()
trimAndLowercase()trim().lowercase()
normalizeEmailAndMask()normalizeEmail().mask()

Order Matters

The order in which you apply transformations can significantly affect the result:
// These produce different results
.map("text").uppercase().trim().to("result1").end()  // "HELLO WORLD"
.map("text").trim().uppercase().to("result2").end()  // "HELLO WORLD"
With the input " hello world ":
  • First example: Uppercases to " HELLO WORLD ", then trims to "HELLO WORLD"
  • Second example: Trims to "hello world", then uppercases to "HELLO WORLD"

Complex Transformation Chains

You can chain as many transformations as needed:
.map("description")
    .stripHtml()          // Remove HTML tags
    .trim()               // Trim whitespace
    .truncate()           // Truncate to 30 chars
    .defaultIfEmpty("No description available")  // Handle empty case
    .to("displayDescription")
    .end()

Creating Custom Transformers

While Platymap includes a wide range of built-in transformers, you can also create custom transformers for your specific needs.

Step 1: Create a Transformer Class

Create a class that implements the ValueTransformer interface:
class PhoneNumberFormatter : ValueTransformer {
    override fun transform(value: Any): Any {
        val str = extractStringValue(value)
        
        // Strip non-digit characters
        val digitsOnly = str.replace(Regex("\\D"), "")
        
        // Apply US phone format
        val formatted = if (digitsOnly.length == 10) {
            "(${digitsOnly.substring(0, 3)}) ${digitsOnly.substring(3, 6)}-${digitsOnly.substring(6)}"
        } else {
            digitsOnly
        }
        
        return when (value) {
            is DataNode.StringValue -> DataNode.StringValue(formatted)
            is DataNode -> DataNode.StringValue(formatted)
            else -> formatted
        }
    }
}

Step 2: Register Your Transformer

Register your transformer with the TransformerRegistry:
TransformerRegistry.register("formatPhone", PhoneNumberFormatter())

Step 3: Use Your Custom Transformer

Now you can use your custom transformer in mappings:
val mapping = Platymap.flow("contact")
    .to("displayContact")
    .map("phone").applyTransformer("formatPhone").to("formattedPhone").end()
    .build()

Creating Parameterized Transformers

For transformers that need configuration parameters:
class CurrencyFormatter(private val currencySymbol: String) : ValueTransformer {
    override fun transform(value: Any): Any {
        val number = extractNumberValue(value)
        val formatted = "$currencySymbol${String.format("%.2f", number)}"
        
        return when (value) {
            is DataNode.StringValue -> DataNode.StringValue(formatted)
            is DataNode -> DataNode.StringValue(formatted)
            else -> formatted
        }
    }
}

// Register with different parameters
TransformerRegistry.register("formatUSD", CurrencyFormatter("$"))
TransformerRegistry.register("formatEUR", CurrencyFormatter("€"))
TransformerRegistry.register("formatGBP", CurrencyFormatter("£"))

Creating Extension Methods

For a more fluent API, you can add extension methods to MappingBuilder:
// Add this extension method to MappingBuilder
fun MappingBuilder.formatPhone(): MappingBuilder = applyTransformer("formatPhone")

// Now you can use it directly
.map("phone").formatPhone().to("formattedPhone").end()

Best Practices

Choose the Right Transformations

  • Use Type-Specific Transformers: Choose transformers designed for your data type (string, number, date, etc.)
  • Consider Data Quality: Add data cleaning transformations first (e.g., trim() before other string operations)
  • Be Mindful of Performance: Complex transformations on large datasets can impact performance

Design Effective Transformation Chains

  • Order Matters: Arrange transformations in a logical order
  • Handle Edge Cases: Use conditional transformations like defaultIfNull() to handle missing data
  • Break Complex Chains: For very complex transformations, consider creating a custom transformer

Naming Conventions

  • Be Consistent: Use consistent naming patterns for your custom transformers
  • Descriptive Names: Choose names that clearly describe what the transformer does
  • Parameterize When Needed: Create parameterized transformers rather than multiple similar transformers

Testing Transformations

  • Test with Edge Cases: Ensure transformers handle null values, empty strings, and other edge cases
  • Verify Composition: Test transformation chains to ensure they produce expected results
  • Document Behavior: Document any special behavior or assumptions in your custom transformers

Troubleshooting

Common Issues and Solutions

IssuePossible CauseSolution
Transformation not appliedMissing .end() after .to()Ensure every mapping ends with .end()
Unexpected data typeTransformer expects different typeAdd appropriate type conversion transformer first
String appears unmodifiedInput is not a stringAdd .toString() before string transformations
Number operations failingInput is not a numberAdd .toDouble() or .toInt() before number operations
Date formatting errorUnrecognized date formatUse .formatDate() with specific patterns
Null resultSource field doesn’t existAdd .defaultIfNull() to provide a default value

Debugging Transformations

  • Log Intermediate Values: Add logging between transformations to see intermediate results
  • Break Down Complex Chains: Test parts of complex transformation chains separately
  • Check Data Types: Verify the data types at each step of the transformation chain

Complete Examples

Product Catalog Example

val productMapping = Platymap.flow("rawProduct")
    .withFormat(Format.JSON)
    .to("displayProduct")
    
    // Basic field mapping with transformations
    .map("product_name").trim().capitalize().to("name").end()
    .map("product_id").toString().padLeft10().to("sku").end()
    
    // Price formatting
    .map("price").toDouble().multiply(1.0).roundToTwoDecimals().to("basePrice").end()
    .map("price").toDouble().multiply(1.2).roundToTwoDecimals().formatUSD().to("retailPrice").end()
    
    // Description formatting
    .map("description").stripHtml().trim().to("plainDescription").end()
    .map("description").stripHtml().trim().truncate().to("shortDescription").end()
    
    // Handle collections
    .map("categories").joinComma().to("categoryList").end()
    .map("tags").sort().joinComma().to("sortedTags").end()
    
    // Generate SEO-friendly URL
    .map("product_name").slugify().to("urlSlug").end()
    
    // Format dates
    .map("created_date").formatDateLong().to("createdOn").end()
    .map("updated_date").formatRelativeDate().to("lastUpdated").end()
    
    // Boolean conversions
    .map("in_stock").toYesNo().to("availability").end()
    .map("is_featured").toActiveInactive().to("featuredStatus").end()
    
    // Defaults for missing values
    .map("brand").defaultIfNull("Generic").to("manufacturer").end()
    .map("color").defaultIfEmpty("Various").to("displayColor").end()
    
    .build()

User Profile Example

val userMapping = Platymap.flow("rawUser")
    .withFormat(Format.JSON)
    .to("userProfile")
    
    // Personal information with privacy protection
    .map("full_name").trim().capitalize().to("displayName").end()
    .map("email").normalizeEmail().to("contactEmail").end()
    .map("phone").applyTransformer("formatPhone").to("contactPhone").end()
    .map("ssn").mask().to("maskedSSN").end()
    
    // Address formatting
    .map("address.line1").trim().to("address.street").end()
    .map("address.line2").defaultIfNull("").to("address.unit").end()
    .map("address.city").trim().capitalize().to("address.city").end()
    .map("address.state").uppercase().to("address.state").end()
    .map("address.zip").trim().to("address.postalCode").end()
    
    // Date formatting
    .map("birthdate").formatDateLong().to("dateOfBirth").end()
    .map("member_since").formatDateShort().to("memberSince").end()
    .map("member_since").formatRelativeDate().to("memberDuration").end()
    
    // Account status
    .map("active").toYesNo().to("accountStatus").end()
    .map("verified").toEnabledDisabled().to("verificationStatus").end()
    
    // Preference handling
    .map("preferences.theme").defaultIfNull("light").to("settings.theme").end()
    .map("preferences.notifications").defaultIfNull(true).toBoolean().to("settings.receiveNotifications").end()
    
    // Complex transformations
    .map("activity_score")
        .toDouble()
        .multiply(10)
        .round()
        .divide(10)
        .to("metrics.engagementScore")
        .end()
    
    .build()

Order Processing Example

val orderMapping = Platymap.flow("rawOrder")
    .withFormat(Format.JSON)
    .to("processedOrder")
    
    // Order metadata
    .map("order_id").toString().padLeft10().to("orderNumber").end()
    .map("status").uppercase().to("orderStatus").end()
    
    // Dates
    .map("order_date").formatDateLong().to("orderDate").end()
    .map("order_date").addOneWeek().formatDateShort().to("estimatedDelivery").end()
    
    // Customer information
    .map("customer.name").trim().capitalize().to("customer.fullName").end()
    .map("customer.email").normalizeEmail().to("customer.contactEmail").end()
    
    // Financial calculations
    .map("items").size().to("itemCount").end()
    .map("subtotal").toDouble().roundToTwoDecimals().to("orderSubtotal").end()
    .map("subtotal").toDouble().multiply(0.1).roundToTwoDecimals().to("tax").end()
    .map("shipping").toDouble().roundToTwoDecimals().to("shippingCost").end()
    
    // Calculate total
    .map("subtotal")
        .toDouble()
        .add(extractNumberValue("tax"))
        .add(extractNumberValue("shipping"))
        .roundToTwoDecimals()
        .to("orderTotal")
        .end()
        
    // Format for display
    .map("orderTotal").formatUSD().to("formattedTotal").end()
    
    // Payment information with masking
    .map("payment.card_number").mask().to("payment.maskedCard").end()
    .map("payment.expiry").to("payment.cardExpiry").end()
    
    // Generate receipt ID
    .map("order_id")
        .transform { "RCP-" + it.toString() + "-" + System.currentTimeMillis().toString().takeLast(6) }
        .to("receiptId")
        .end()
    
    .build()
These examples demonstrate how Platymap’s transformation system can be used to handle a wide variety of real-world data transformation scenarios, from simple string formatting to complex business logic. By combining the power of built-in transformers with the ability to create custom transformers, Platymap provides a flexible, expressive way to transform your data during mapping operations.