Why: The GoCardless API only accepts string values for metadata, but the Java types previously allowed Map<String, Object>. This caused confusing runtime errors. The new version fixes the types to match the API requirements.
Impact: Code that passes non-string values to metadata fields will fail to compile.
The easiest way to migrate is using the new MetadataHelper class:
import com.gocardless.helpers.MetadataHelper;
import java.util.HashMap;
import java.util.Map;
// ❌ BEFORE - Compiled but failed at runtime
Map<String, Object> metadata = new HashMap<>();
metadata.put("user_id", 12345); // Integer
metadata.put("is_active", true); // Boolean
metadata.put("tags", Arrays.asList("vip", "premium")); // List
client.customers().create()
.withEmail("user@example.com")
.withMetadata(metadata) // Runtime error!
.execute();
// ✅ AFTER - One function call
client.customers().create()
.withEmail("user@example.com")
.withMetadata(MetadataHelper.toMetadata(metadata)) // Converts all values
.execute();If you prefer explicit control:
Map<String, String> metadata = new HashMap<>();
metadata.put("user_id", String.valueOf(12345)); // "12345"
metadata.put("is_active", String.valueOf(true)); // "true"
Gson gson = new Gson();
metadata.put("tags", gson.toJson(Arrays.asList("vip", "premium"))); // JSON
client.customers().create()
.withEmail("user@example.com")
.withMetadata(metadata)
.execute();Converts a map with mixed value types to metadata format:
Map<String, Object> data = new HashMap<>();
data.put("user_id", 12345);
data.put("is_premium", true);
data.put("signup_date", new Date());
data.put("preferences", Map.of("theme", "dark"));
Map<String, String> metadata = MetadataHelper.toMetadata(data);
// Result: {
// "user_id": "12345",
// "is_premium": "true",
// "signup_date": "Mon Jan 15 10:30:00 GMT 2024",
// "preferences": "{\"theme\":\"dark\"}"
// }Converts a single value:
MetadataHelper.toMetadataValue(12345); // "12345"
MetadataHelper.toMetadataValue(true); // "true"
MetadataHelper.toMetadataValue(Arrays.asList("vip")); // "[\"vip\"]"
MetadataHelper.toMetadataValue(Map.of("theme", "dark")); // "{\"theme\":\"dark\"}"Check if metadata is valid:
Map<String, Object> metadata = new HashMap<>();
metadata.put("key", "value");
if (MetadataHelper.isValidMetadata(metadata)) {
// All values are strings
client.customers().create()
.withMetadata((Map<String, String>) (Object) metadata)
.execute();
}Parse metadata values back to their original types:
Customer customer = client.customers().get("CU123").execute();
Map<String, String> metadata = customer.getMetadata();
// Parse back to original types
Integer userId = MetadataHelper.parseMetadataValue(
metadata.get("user_id"),
Integer.class
); // 12345
Boolean isActive = MetadataHelper.parseMetadataValue(
metadata.get("is_active"),
Boolean.class
); // true
// For generic types, use TypeToken
List<String> tags = MetadataHelper.parseMetadataValue(
metadata.get("tags"),
new TypeToken<List<String>>(){}.getType()
); // ["vip", "premium"]