This document describes the available merge strategies in ObjectMerger. Each section provides a description, a full example (inputs, definition, result), and a Java code snippet demonstrating usage.
- Standard Strategy
- Sum Strategy
- Concatenate Strategy
- Maximum Strategy
- Minimum Strategy
- Average Strategy
- Priority Strategy
- Conditional Strategy (MVEL)
- Conditional Strategy (GraalJS)
- GraalJS Strategy
- MVEL Strategy
- List Strategy
- Map Strategy
- Nested Strategy
Strategy Name: standard
Description: Picks the first non-null value found in the source list. If all are null, returns the default value. This is the default strategy if none is specified.
Example Scenario: Merging a title field where "Legacy System" (json1) takes precedence over "New System" (json2) because json1 is first in the list.
json1
{
"title": "Legacy System"
}json2
{
"title": "New System"
}{
"definitions": {
"title": {
"strategy": "standard",
"defaultValue": "Unknown"
}
}
}{
"title": "Legacy System"
}StandardFieldDefinition<String> titleDef = StandardFieldDefinition.<String>builder()
.defaultValue("Unknown")
.build();Strategy Name: sum
Description: Sums up numeric values from all sources.
Example Scenario: Calculating a total score from two sources.
json1
{
"score": 10
}json2
{
"score": 20
}{
"definitions": {
"score": {
"strategy": "sum",
"defaultValue": 0
}
}
}{
"score": 30
}StandardFieldDefinition<Number> scoreDef = StandardFieldDefinition.<Number>builder()
.strategy("sum")
.defaultValue(0)
.build();Strategy Name: concatenate
Description: Joins string values from all sources with a delimiter (default ,).
Example Scenario: Merging tags from different sources.
json1
{
"tags": "tag1"
}json2
{
"tags": "tag2"
}{
"definitions": {
"tags": {
"strategy": "concatenate",
"defaultValue": ""
}
}
}{
"tags": "tag1,tag2"
}StandardFieldDefinition<String> tagsDef = StandardFieldDefinition.<String>builder()
.strategy("concatenate")
.defaultValue("") // or any delimiter
.build();Strategy Name: maximum
Description: Selects the maximum value from all sources (Numbers or Comparables).
Example Scenario: Determining the highest access level found.
json1
{
"level": 5
}json2
{
"level": 8
}{
"definitions": {
"level": {
"strategy": "maximum",
"defaultValue": 0
}
}
}{
"level": 8
}StandardFieldDefinition<Number> levelDef = StandardFieldDefinition.<Number>builder()
.strategy("maximum")
.defaultValue(0)
.build();Strategy Name: minimum
Description: Selects the minimum value from all sources.
Example Scenario: Finding the lowest price across vendors.
json1
{
"price": 99.99
}json2
{
"price": 45.50
}{
"definitions": {
"price": {
"strategy": "minimum",
"defaultValue": 0.0
}
}
}{
"price": 45.5
}StandardFieldDefinition<Number> priceDef = StandardFieldDefinition.<Number>builder()
.strategy("minimum")
.defaultValue(0.0)
.build();Strategy Name: average
Description: Calculates the arithmetic mean of numeric values.
Example Scenario: Averaging ratings from different reviews.
json1
{
"rating": 4.0
}json2
{
"rating": 5.0
}{
"definitions": {
"rating": {
"strategy": "average",
"defaultValue": 0.0
}
}
}{
"rating": 4.5
}StandardFieldDefinition<Number> ratingDef = StandardFieldDefinition.<Number>builder()
.strategy("average")
.defaultValue(0.0)
.build();Strategy Name: priority
Description: Selects a value based on explicit source priority ranking. Lower number indicates higher priority.
Example Scenario: Publishing status where "json2" (publisher) overrides "json1" (drafter).
json1
{
"status": "DRAFT"
}json2
{
"status": "PUBLISHED"
}{
"definitions": {
"status": {
"strategy": "priority",
"defaultValue": "UNKNOWN",
"priority": {
"json2": 1,
"json1": 2
}
}
}
}{
"status": "PUBLISHED"
}Map<String, Integer> priorities = new HashMap<>();
priorities.put("json2", 1);
priorities.put("json1", 2);
PriorityFieldDefinition<String> statusDef = PriorityFieldDefinition.<String>builder()
.strategy("priority")
.defaultValue("UNKNOWN")
.priority(priorities)
.build();Strategy Name: conditional
Description: Evaluates expressions to decide which sub-strategy to use. This documentation covers usage with the MVEL Extension (objectmerger-mvel).
Context Variables: sources (List), values (Map<SourceLabel, ValueOfCurrentField>).
Example Scenario: If age is 18 (adult), prioritize json1 (where value is 'adult').
json1
{
"age": 18,
"category": "adult"
}json2
{
"age": 18,
"category": "minor"
}{
"definitions": {
"category": {
"strategy": "conditional",
"defaultValue": "unknown",
"cases": [
{
"condition": "values['json1'] == 'adult'",
"useStrategy": { "strategy": "priority", "priority": {"json1": 1} }
}
]
}
}
}{
"category": "adult"
}ConditionCase<String> adultCase = new ConditionCase<>();
adultCase.setCondition("values['json1'] == 'adult'"); // MVEL Syntax
Map<String, Integer> p = new HashMap<>();
p.put("json1", 1);
PriorityFieldDefinition<String> priorityDef = PriorityFieldDefinition.<String>builder()
.priority(p)
.build();
adultCase.setUseStrategy(priorityDef);
ConditionalFieldDefinition<String> categoryDef = ConditionalFieldDefinition.<String>builder()
.defaultValue("unknown")
.cases(List.of(adultCase))
.build();Strategy Name: conditional
Description: Evaluates expressions to decide which sub-strategy to use. This documentation covers usage with the GraalJS Extension (objectmerger-graaljs).
Context Variables: sources (List), values (Map<SourceLabel, ValueOfCurrentField>).
Example Scenario: If age is 18 (adult), prioritize json1 (where value is 'adult').
json1
{
"age": 18,
"category": "adult"
}json2
{
"age": 18,
"category": "minor"
}{
"definitions": {
"category": {
"strategy": "conditional",
"defaultValue": "unknown",
"cases": [
{
"condition": "values.get('json1') == 'adult'",
"useStrategy": { "strategy": "priority", "priority": {"json1": 1} }
}
]
}
}
}{
"category": "adult"
}ConditionCase<String> adultCase = new ConditionCase<>();
adultCase.setCondition("values.get('json1') == 'adult'"); // JS Syntax
// ... (Rest of setup remains the same) ...
ConditionalFieldDefinition<String> categoryDef = ConditionalFieldDefinition.<String>builder()
.defaultValue("unknown")
.cases(List.of(adultCase))
.build();Strategy Name: mvel
Description: Executes a complex MVEL expression to calculate the value.
Context Variables: sources (Map<SourceLabel, SourceObject>).
Example Scenario: Calculating a final price by applying a discount rate from the same source.
json1
{
"price": 100,
"discount": 0.1
}{
"definitions": {
"finalPrice": {
"strategy": "mvel",
"expression": "sources['json1']['price'] * (1.0 - sources['json1']['discount'])",
"defaultValue": 0.0
}
}
}{
"finalPrice": 90.0
}MvelFieldDefinition mvelDef = MvelFieldDefinition.builder()
.expression("sources['json1']['price'] * (1.0 - sources['json1']['discount'])")
.defaultValue(0.0)
.build();Strategy Name: graaljs
Description: Executes a complex JavaScript expression (via GraalVM Polyglot) to calculate the value.
Context Variables: sources (Map<SourceLabel, SourceObject>).
Example Scenario: Calculating a final price by applying a discount rate from the same source.
json1
{
"price": 100,
"discount": 0.1
}{
"definitions": {
"finalPrice": {
"strategy": "graaljs",
"expression": "sources.get('json1').price * (1.0 - sources.get('json1').discount)",
"defaultValue": 0.0
}
}
}{
"finalPrice": 90.0
}GraalJsFieldDefinition jsDef = GraalJsFieldDefinition.builder()
.expression("sources.get('json1').price * (1.0 - sources.get('json1').discount)")
.defaultValue(0.0)
.build();Strategy Name: mergeList
Description: Merges list field by grouping items by an identifier field.
Example Scenario: Merging lists of items where items are identified by "id".
json1
{
"items": [
{ "id": "1", "name": "Item 1" }
]
}json2
{
"items": [
{ "id": "2", "name": "Item 2" }
]
}{
"definitions": {
"items": {
"strategy": "mergeList",
"identifyBy": "id",
"itemMergeDefinition": {
"definitions": {
"name": {
"strategy": "standard",
"defaultValue": "Unknown"
}
}
}
}
}
}{
"items": [
{ "id": "1", "name": "Item 1" },
{ "id": "2", "name": "Item 2" }
]
}ListFieldDefinition<List<Object>> itemsDef = ListFieldDefinition.<List<Object>>builder()
.identifyBy("id")
.build();
ItemMergeDefinition itemDef = new ItemMergeDefinition();
// ... configure itemDef definitions ...
itemsDef.setItemMergeDefinition(itemDef);Strategy Name: mergeMap
Description: Merges map fields by union of keys.
Example Scenario: merging translation maps.
json1
{
"translations": {
"en": "Hello"
}
}json2
{
"translations": {
"de": "Hallo"
}
}{
"definitions": {
"translations": {
"strategy": "mergeMap"
}
}
}{
"translations": {
"en": "Hello",
"de": "Hallo"
}
}MapFieldDefinition<Map<Object, Object>> transDef = MapFieldDefinition.<Map<Object, Object>>builder()
.build();Strategy Name: nested
Description: Recursively merges nested POJO objects based on a nested definition. This allows for granular control over sub-field merging.
Example Scenario: Merging an address where the street comes from an API source (json2) and the zip code from a database source (json1).
json1 (db)
{
"address": {
"street": "Old St",
"zip": "12345"
}
}json2 (api)
{
"address": {
"street": "New St",
"zip": "99999"
}
}{
"definitions": {
"address": {
"strategy": "nested",
"nestedDefinition": {
"definitions": {
"street": {
"strategy": "priority",
"priority": {"json2": 1, "json1": 2}
},
"zip": {
"strategy": "priority",
"priority": {"json1": 1, "json2": 2}
}
}
}
}
}
}{
"address": {
"street": "New St",
"zip": "12345"
}
}NestedFieldDefinition<Address> addressDef = NestedFieldDefinition.<Address>builder()
.nestedDefinition(nestedMergeDefinition)
.build();