0
# Parameter Transformation
1
2
Custom parameter types for converting step parameters from strings to domain objects, with extensive configuration options. Cucumber-Java provides a sophisticated transformation system including custom parameter types, default transformers, and data table transformations.
3
4
## Capabilities
5
6
### Custom Parameter Types
7
8
Register custom parameter types to convert strings from step definitions into domain objects using regular expressions.
9
10
```java { .api }
11
/**
12
* Register custom parameter types for Cucumber expressions
13
* @param value Regular expression pattern for matching
14
* @param name Parameter type name (default: method name)
15
* @param preferForRegexMatch Preferential type for regex matching (default: false)
16
* @param useForSnippets Use in snippet generation (default: false)
17
* @param useRegexpMatchAsStrongTypeHint Provide strong type hint (default: false)
18
*/
19
@ParameterType(value = "regex",
20
name = "typeName",
21
preferForRegexMatch = false,
22
useForSnippets = false,
23
useRegexpMatchAsStrongTypeHint = false)
24
public CustomType parameterTransformer(String input) {
25
return new CustomType(input);
26
}
27
28
/**
29
* Multi-capture group parameter type
30
*/
31
@ParameterType("(\\d+)-(\\d+)-(\\d+)")
32
public LocalDate date(String year, String month, String day) {
33
return LocalDate.of(Integer.parseInt(year),
34
Integer.parseInt(month),
35
Integer.parseInt(day));
36
}
37
```
38
39
**Usage Examples:**
40
41
```java
42
import io.cucumber.java.ParameterType;
43
import java.time.LocalDate;
44
import java.time.format.DateTimeFormatter;
45
46
public class ParameterTypes {
47
48
// Simple parameter type
49
@ParameterType("\\d{4}-\\d{2}-\\d{2}")
50
public LocalDate date(String dateString) {
51
return LocalDate.parse(dateString);
52
}
53
54
// Custom name parameter type
55
@ParameterType(value = "\\d+", name = "number")
56
public BigInteger bigNumber(String numberString) {
57
return new BigInteger(numberString);
58
}
59
60
// Multi-capture group parameter type
61
@ParameterType("(\\d{1,2})/(\\d{1,2})/(\\d{4})")
62
public LocalDate usDate(String month, String day, String year) {
63
return LocalDate.of(Integer.parseInt(year),
64
Integer.parseInt(month),
65
Integer.parseInt(day));
66
}
67
68
// Enum parameter type
69
@ParameterType("red|green|blue")
70
public Color color(String colorName) {
71
return Color.valueOf(colorName.toUpperCase());
72
}
73
74
// Complex object parameter type
75
@ParameterType("([A-Za-z]+)\\s+([A-Za-z]+)\\s+<([^>]+)>")
76
public Person person(String firstName, String lastName, String email) {
77
return new Person(firstName, lastName, email);
78
}
79
80
// Parameter type for snippets
81
@ParameterType(value = "\\d+\\.\\d{2}", useForSnippets = true)
82
public Money money(String amount) {
83
return new Money(new BigDecimal(amount));
84
}
85
86
// Preferential parameter type
87
@ParameterType(value = "\\d+", preferForRegexMatch = true)
88
public CustomNumber customNumber(String number) {
89
return new CustomNumber(Integer.parseInt(number));
90
}
91
}
92
93
// Usage in step definitions
94
@Given("the date is {date}")
95
public void the_date_is(LocalDate date) {
96
// date is automatically converted from string
97
}
98
99
@When("I transfer {money} to {person}")
100
public void transfer_money(Money amount, Person recipient) {
101
// Both parameters automatically converted
102
}
103
```
104
105
### Default Parameter Transformer
106
107
Register default parameter transformer for handling all unmatched parameter conversions.
108
109
```java { .api }
110
/**
111
* Register default parameter transformer
112
* Method signatures supported:
113
* - String, Type -> Object (transform from string)
114
* - Object, Type -> Object (transform from any object)
115
*/
116
@DefaultParameterTransformer
117
public Object defaultTransformer(String fromValue, Type toValueType) {
118
// Default transformation logic
119
return transformedValue;
120
}
121
122
/**
123
* Alternative signature for object transformation
124
*/
125
@DefaultParameterTransformer
126
public Object defaultTransformer(Object fromValue, Type toValueType) {
127
// Transform from any object type
128
return transformedValue;
129
}
130
```
131
132
**Usage Examples:**
133
134
```java
135
import io.cucumber.java.DefaultParameterTransformer;
136
import com.fasterxml.jackson.databind.ObjectMapper;
137
import java.lang.reflect.Type;
138
139
public class DefaultTransformers {
140
141
private final ObjectMapper objectMapper = new ObjectMapper();
142
143
// JSON-based default transformer using Jackson
144
@DefaultParameterTransformer
145
public Object defaultTransformer(String fromValue, Type toValueType) {
146
try {
147
// Try JSON transformation first
148
return objectMapper.readValue(fromValue,
149
objectMapper.constructType(toValueType));
150
} catch (Exception e) {
151
// Fallback to string-based transformation
152
if (toValueType.equals(Integer.class)) {
153
return Integer.valueOf(fromValue);
154
}
155
if (toValueType.equals(Boolean.class)) {
156
return Boolean.valueOf(fromValue);
157
}
158
if (toValueType.equals(LocalDate.class)) {
159
return LocalDate.parse(fromValue);
160
}
161
return fromValue; // Return as string if no transformation
162
}
163
}
164
165
// Enum-aware default transformer
166
@DefaultParameterTransformer
167
public Object enumTransformer(String fromValue, Type toValueType) {
168
if (toValueType instanceof Class && ((Class<?>) toValueType).isEnum()) {
169
Class<? extends Enum> enumClass = (Class<? extends Enum>) toValueType;
170
return Enum.valueOf(enumClass, fromValue.toUpperCase());
171
}
172
return fromValue;
173
}
174
}
175
176
// Usage - these will use default transformer
177
@Given("the status is {Status}") // Uses enum transformer
178
public void the_status_is(Status status) { }
179
180
@When("the config is {Config}") // Uses JSON transformer
181
public void the_config_is(Config config) { }
182
```
183
184
### Data Table Type Transformers
185
186
Register transformers for converting data table entries into custom objects.
187
188
```java { .api }
189
/**
190
* Register data table type transformer
191
* @param replaceWithEmptyString Values to replace with empty string (default: {})
192
*/
193
@DataTableType(replaceWithEmptyString = {"[blank]", "[empty]"})
194
public CustomObject tableEntryTransformer(Map<String, String> entry) {
195
return new CustomObject(entry);
196
}
197
198
/**
199
* Data table type for list transformation
200
*/
201
@DataTableType
202
public CustomObject listTransformer(List<String> row) {
203
return new CustomObject(row.get(0), row.get(1));
204
}
205
206
/**
207
* Data table type for single cell transformation
208
*/
209
@DataTableType
210
public CustomObject cellTransformer(String cell) {
211
return new CustomObject(cell);
212
}
213
```
214
215
**Usage Examples:**
216
217
```java
218
import io.cucumber.java.DataTableType;
219
import java.util.Map;
220
import java.util.List;
221
222
public class DataTableTypes {
223
224
// Transform table rows to Person objects
225
@DataTableType
226
public Person personEntry(Map<String, String> entry) {
227
return new Person(
228
entry.get("name"),
229
entry.get("email"),
230
Integer.parseInt(entry.get("age"))
231
);
232
}
233
234
// Transform with empty string replacement
235
@DataTableType(replaceWithEmptyString = {"", "[empty]", "null"})
236
public Product productEntry(Map<String, String> entry) {
237
return new Product(
238
entry.get("name"),
239
entry.get("description").isEmpty() ? null : entry.get("description"),
240
new BigDecimal(entry.get("price"))
241
);
242
}
243
244
// Transform list rows to objects
245
@DataTableType
246
public Coordinate coordinateRow(List<String> row) {
247
return new Coordinate(
248
Double.parseDouble(row.get(0)), // x
249
Double.parseDouble(row.get(1)) // y
250
);
251
}
252
253
// Single cell transformation
254
@DataTableType
255
public Currency currencyCell(String cell) {
256
return Currency.getInstance(cell);
257
}
258
}
259
260
// Usage in step definitions
261
@Given("the following users exist:")
262
public void users_exist(List<Person> users) {
263
// Each table row automatically converted to Person
264
}
265
266
@When("I create products:")
267
public void create_products(List<Product> products) {
268
// Each table row automatically converted to Product
269
}
270
```
271
272
### Default Data Table Transformers
273
274
Register default transformers for data table entries and cells.
275
276
```java { .api }
277
/**
278
* Register default data table entry transformer
279
* @param headersToProperties Convert headers to properties format (default: true)
280
* @param replaceWithEmptyString Values to replace with empty string (default: {})
281
*/
282
@DefaultDataTableEntryTransformer(headersToProperties = true,
283
replaceWithEmptyString = {})
284
public Object defaultEntryTransformer(Map<String, String> entry, Type type) {
285
return transformedEntry;
286
}
287
288
/**
289
* Register default data table cell transformer
290
* @param replaceWithEmptyString Values to replace with empty string (default: {})
291
*/
292
@DefaultDataTableCellTransformer(replaceWithEmptyString = {})
293
public Object defaultCellTransformer(String cell, Type type) {
294
return transformedCell;
295
}
296
```
297
298
**Usage Examples:**
299
300
```java
301
import io.cucumber.java.DefaultDataTableEntryTransformer;
302
import io.cucumber.java.DefaultDataTableCellTransformer;
303
import com.fasterxml.jackson.databind.ObjectMapper;
304
import java.lang.reflect.Type;
305
306
public class DefaultDataTableTransformers {
307
308
private final ObjectMapper objectMapper = new ObjectMapper();
309
310
// JSON-based default entry transformer
311
@DefaultDataTableEntryTransformer
312
public Object defaultEntryTransformer(Map<String, String> entry, Type type) {
313
try {
314
// Convert map to JSON then to target type
315
String json = objectMapper.writeValueAsString(entry);
316
return objectMapper.readValue(json,
317
objectMapper.constructType(type));
318
} catch (Exception e) {
319
throw new RuntimeException("Failed to transform entry: " + entry, e);
320
}
321
}
322
323
// Property-style header conversion
324
@DefaultDataTableEntryTransformer(headersToProperties = true)
325
public Object propertyEntryTransformer(Map<String, String> entry, Type type) {
326
// Headers like "First Name" become "firstName" properties
327
Map<String, String> propertyMap = entry.entrySet().stream()
328
.collect(Collectors.toMap(
329
e -> toCamelCase(e.getKey()),
330
Map.Entry::getValue
331
));
332
333
return createObjectFromProperties(propertyMap, type);
334
}
335
336
// Default cell transformer with multiple replacements
337
@DefaultDataTableCellTransformer(replaceWithEmptyString = {
338
"[blank]", "[empty]", "null", "N/A", ""
339
})
340
public Object defaultCellTransformer(String cell, Type type) {
341
// Handle common type conversions
342
if (type.equals(Integer.class) && !cell.isEmpty()) {
343
return Integer.valueOf(cell);
344
}
345
if (type.equals(Boolean.class)) {
346
return Boolean.valueOf(cell);
347
}
348
if (type.equals(LocalDate.class) && !cell.isEmpty()) {
349
return LocalDate.parse(cell);
350
}
351
return cell;
352
}
353
354
private String toCamelCase(String header) {
355
return Arrays.stream(header.split("\\s+"))
356
.map(word -> word.toLowerCase())
357
.collect(Collectors.joining())
358
.replaceFirst("^.", header.substring(0, 1).toLowerCase());
359
}
360
}
361
```
362
363
### Doc String Type Transformers
364
365
Register transformers for converting doc strings into custom objects.
366
367
```java { .api }
368
/**
369
* Register doc string type transformer
370
* @param contentType Content type for matching (default: "" - uses method name)
371
*/
372
@DocStringType(contentType = "json")
373
public CustomObject docStringTransformer(String docString) {
374
return parseDocString(docString);
375
}
376
377
/**
378
* Doc string type using method name as content type
379
*/
380
@DocStringType
381
public XmlDocument xml(String docString) {
382
return XmlDocument.parse(docString);
383
}
384
```
385
386
**Usage Examples:**
387
388
```java
389
import io.cucumber.java.DocStringType;
390
import com.fasterxml.jackson.databind.JsonNode;
391
import com.fasterxml.jackson.databind.ObjectMapper;
392
393
public class DocStringTypes {
394
395
private final ObjectMapper objectMapper = new ObjectMapper();
396
397
// JSON doc string transformer
398
@DocStringType
399
public JsonNode json(String docString) {
400
try {
401
return objectMapper.readTree(docString);
402
} catch (Exception e) {
403
throw new RuntimeException("Invalid JSON: " + docString, e);
404
}
405
}
406
407
// XML doc string transformer
408
@DocStringType
409
public Document xml(String docString) {
410
try {
411
DocumentBuilder builder = DocumentBuilderFactory
412
.newInstance()
413
.newDocumentBuilder();
414
return builder.parse(new InputSource(new StringReader(docString)));
415
} catch (Exception e) {
416
throw new RuntimeException("Invalid XML: " + docString, e);
417
}
418
}
419
420
// Custom content type
421
@DocStringType(contentType = "yaml")
422
public Map<String, Object> yamlContent(String docString) {
423
return yamlParser.parse(docString);
424
}
425
426
// Configuration object from properties
427
@DocStringType(contentType = "properties")
428
public Properties propertiesContent(String docString) {
429
Properties props = new Properties();
430
try {
431
props.load(new StringReader(docString));
432
return props;
433
} catch (Exception e) {
434
throw new RuntimeException("Invalid properties: " + docString, e);
435
}
436
}
437
}
438
439
// Usage in step definitions
440
@Given("the following JSON configuration:")
441
public void json_configuration(JsonNode config) {
442
// DocString automatically parsed as JSON
443
}
444
445
@When("I send the XML request:")
446
public void send_xml_request(Document xmlDoc) {
447
// DocString automatically parsed as XML
448
}
449
450
@Then("the YAML response should be:")
451
public void yaml_response(Map<String, Object> yaml) {
452
// DocString automatically parsed as YAML
453
}
454
```
455
456
### Data Table Transposition
457
458
Use the `@Transpose` annotation to transpose data tables before transformation.
459
460
```java { .api }
461
/**
462
* Transpose annotation for data table parameters
463
* @param value Whether to transpose the table (default: true)
464
*/
465
public void stepWithTransposedTable(@Transpose DataTable table) { }
466
467
/**
468
* Transpose with explicit value
469
*/
470
public void stepWithTransposedTable(@Transpose(true) List<Map<String, String>> data) { }
471
472
/**
473
* Conditional transposition
474
*/
475
public void stepWithConditionalTranspose(@Transpose(false) DataTable table) { }
476
```
477
478
**Usage Examples:**
479
480
```java
481
import io.cucumber.java.Transpose;
482
import io.cucumber.datatable.DataTable;
483
484
public class TransposeExamples {
485
486
// Transpose data table for vertical layout
487
@Given("the user has the following profile:")
488
public void user_profile(@Transpose DataTable profileData) {
489
// Original table: | name | John |
490
// | email | john@...|
491
// | age | 30 |
492
//
493
// After transpose: | name | email | age |
494
// | John | john@... | 30 |
495
}
496
497
// Transpose list of maps
498
@Given("the settings are configured:")
499
public void settings_configured(@Transpose List<Map<String, String>> settings) {
500
// Transposed before converting to list of maps
501
for (Map<String, String> setting : settings) {
502
String key = setting.get("key");
503
String value = setting.get("value");
504
}
505
}
506
507
// Conditional transpose
508
@Given("the matrix data:")
509
public void matrix_data(@Transpose(false) DataTable matrix) {
510
// Table is NOT transposed
511
}
512
513
// Transpose with custom objects
514
@Given("the product attributes:")
515
public void product_attributes(@Transpose List<Attribute> attributes) {
516
// Table transposed then converted to Attribute objects
517
}
518
}
519
```