Moving forward, we will integrate loginto the DSL to simplify and enhance the debugging and logging process.
You can use the map operation to log values without changing the mapping:
val mapping = Platymap.flow("source")
.to("target")
.map("customer.name").transform {
println("Name: $it") // Log the value
it // Return unchanged
}.to("name")
.build()
Using Try-Catch for Error Handling
Wrap your mapping execution in try-catch blocks to handle errors:
try {
val result = mapping.execute(sourceData)
// Process result
} catch (e: MappingExecutionException) {
println("Mapping error: ${e.message}")
e.printStackTrace()
}
Validating Mappings
Before executing a mapping with real data, you can validate it with test data:
fun validateMapping(mapping: Mapping, testData: Any): Boolean {
try {
mapping.execute(testData)
return true
} catch (e: Exception) {
println("Validation failed: ${e.message}")
return false
}
}
Debugging DSL Construction
If you’re having issues with the DSL syntax, break it down into smaller parts:
// Instead of a single chain
val mapping = Platymap.flow("source")
.to("target")
.map("a").to("x")
.map("b").to("y")
.build()
// Break it down
val sourceBuilder = Platymap.flow("source")
val targetBuilder = sourceBuilder.to("target")
val builderAfterFirstMap = targetBuilder.map("a").to("x")
val builderAfterSecondMap = builderAfterFirstMap.map("b").to("y")
val mapping = builderAfterSecondMap.build()
This allows you to inspect each builder step and understand what’s happening.
Common Issues and Solutions
-
Null Values: If a source path doesn’t exist, the value will be null. Use transformations to provide defaults:
.map("optional.field").transform { it ?: "default" }.to("required.field")
-
Type Mismatches: Be careful with type casting. Use safe casts or checks:
.map("value").transform {
when (it) {
is Number -> it.toDouble()
is String -> it.toDoubleOrNull() ?: 0.0
else -> 0.0
}
}.to("numericValue")
-
Path Syntax: Make sure you’re using the correct path syntax:
- Simple path:
"customer.name"
- Array index:
"items[0].name"
- Variable reference:
"$variable.property"
- Literal value:
"'static text'"
-
Builder Chain: Each builder method returns either the same builder (for configuration) or a new builder (for a new context). Make sure you understand the builder flow.
Best Practices
Organizing Mappings
For large projects, organize your mappings into separate files or classes:
object CustomerMappings {
fun createCustomerToUserMapping(): Mapping {
return Platymap.flow("customer")
.to("user")
.map("name").to("fullName")
// More mappings...
.build()
}
fun createUserToCustomerMapping(): Mapping {
// Reverse mapping
}
}
Reusing Mapping Logic
Extract common mapping logic into functions:
fun TargetBuilder.mapAddress(sourcePrefix: String, targetPrefix: String): TargetBuilder {
return this
.map("$sourcePrefix.street").to("$targetPrefix.streetAddress")
.map("$sourcePrefix.city").to("$targetPrefix.city")
.map("$sourcePrefix.state").to("$targetPrefix.state")
.map("$sourcePrefix.zip").to("$targetPrefix.postalCode")
}
// Usage
val mapping = Platymap.flow("customer")
.to("user")
.mapAddress("address", "contactAddress")
.build()
Error Handling
Add error handling to your transformations:
.map("value").transform { value ->
try {
// Risky operation
(value as String).toInt()
} catch (e: Exception) {
// Fallback
0
}
}.to("safeValue")
Testing Mappings
Create unit tests for your mappings:
@Test
fun testCustomerToUserMapping() {
val mapping = CustomerMappings.createCustomerToUserMapping()
val customer = mapOf(
"name" to "John Doe",
"email" to "[email protected]"
)
val result = mapping.execute(customer)
// Assert on the result
val resultMap = result as DataNode.ObjectNode
assertEquals("John Doe", resultMap.get("fullName")?.toString())
assertEquals("[email protected]", resultMap.get("contactEmail")?.toString())
}
- Reuse Mappings: Create mappings once and reuse them.
- Avoid Excessive Transformations: Only transform data when necessary.
- Use Bulk Operations: Use
mapAll for multiple fields when possible.
- Cache Path Accessors: For repeated access to the same path.
Documentation
Document your mappings, especially for complex transformations:
/**
* Maps a customer object to a user object.
*
* Source fields:
* - name: Customer's full name
* - email: Customer's email address
* - address: Nested object with street, city, state, zip
*
* Target fields:
* - fullName: User's full name (from customer.name)
* - contactEmail: User's email (from customer.email)
* - location: Nested object with formatted address information
*/
fun createCustomerToUserMapping(): Mapping {
return Platymap.flow("customer")
.to("user")
// Mapping rules...
.build()
}