Joda Datetime Custom Serializer and Deserializer
By Taking help of Custom Type Adapters we can create a comprehensive custom serializers and deserializers for Joda DateTime with Gson. This will handle both serialization (DateTime to JSON) and deserialization (JSON to DateTime) with proper error handling and format control.
Create the DateTime Type Adapter
import com.google.gson.*;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.ISODateTimeFormat;
import java.lang.reflect.Type;
public class JodaDateTimeAdapter implements JsonSerializer<DateTime>, JsonDeserializer<DateTime> {
private final DateTimeFormatter formatter;
public JodaDateTimeAdapter() {
// Using ISO format as default
this.formatter = ISODateTimeFormat.dateTime();
}
public JodaDateTimeAdapter(DateTimeFormatter formatter) {
this.formatter = formatter;
}
@Override
public JsonElement serialize(DateTime src, Type typeOfSrc, JsonSerializationContext context) {
try {
return new JsonPrimitive(formatter.print(src));
} catch (Exception e) {
throw new JsonParseException("Error serializing DateTime", e);
}
}
@Override
public DateTime deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
try {
String dateTimeStr = json.getAsString();
return formatter.parseDateTime(dateTimeStr);
} catch (IllegalArgumentException e) {
throw new JsonParseException("Error parsing DateTime", e);
}
}
}
With Multiple Format Support
import com.google.gson.*;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.DateTimeFormatterBuilder;
import org.joda.time.format.ISODateTimeFormat;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
public class AdvancedJodaDateTimeAdapter implements JsonSerializer<DateTime>, JsonDeserializer<DateTime> {
private final List<DateTimeFormatter> formatters;
private final DateTimeFormatter primaryFormatter;
public AdvancedJodaDateTimeAdapter() {
this.formatters = new ArrayList<>();
// Primary formatter for serialization
this.primaryFormatter = ISODateTimeFormat.dateTime();
// Add multiple formats for deserialization attempts
formatters.add(ISODateTimeFormat.dateTime());
formatters.add(ISODateTimeFormat.dateTimeNoMillis());
formatters.add(ISODateTimeFormat.basicDateTime());
formatters.add(ISODateTimeFormat.basicDateTimeNoMillis());
formatters.add(new DateTimeFormatterBuilder()
.appendPattern("yyyy-MM-dd HH:mm:ss")
.toFormatter());
}
@Override
public JsonElement serialize(DateTime src, Type typeOfSrc, JsonSerializationContext context) {
try {
return new JsonPrimitive(primaryFormatter.print(src));
} catch (Exception e) {
throw new JsonParseException("Error serializing DateTime: " + e.getMessage(), e);
}
}
@Override
public DateTime deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
String dateTimeStr = json.getAsString();
// Try each formatter in sequence
for (DateTimeFormatter formatter : formatters) {
try {
return formatter.parseDateTime(dateTimeStr);
} catch (IllegalArgumentException ignored) {
// Continue to next formatter if this one fails
}
}
throw new JsonParseException("Unable to parse DateTime with any of the registered formats: "
+ dateTimeStr);
}
}
Usage Examples
public class JodaDateTimeExample {
public static void main(String[] args) {
// Example class with DateTime field
class Event {
private String name;
private DateTime timestamp;
public Event(String name, DateTime timestamp) {
this.name = name;
this.timestamp = timestamp;
}
// Getters and setters
}
// Basic usage
Gson gson = new GsonBuilder()
.registerTypeAdapter(DateTime.class, new JodaDateTimeAdapter())
.create();
// Advanced usage with multiple format support
Gson advancedGson = new GsonBuilder()
.registerTypeAdapter(DateTime.class, new AdvancedJodaDateTimeAdapter())
.create();
// Create test data
Event event = new Event("Meeting", new DateTime());
// Serialization
String json = gson.toJson(event);
System.out.println("Serialized: " + json);
// Deserialization
Event deserializedEvent = gson.fromJson(json, Event.class);
System.out.println("Deserialized timestamp: " + deserializedEvent.timestamp.toString());
// Test with different date formats
String[] testDates = {
"{\"timestamp\": \"2023-04-01T14:30:00.000Z\"}",
"{\"timestamp\": \"2023-04-01T14:30:00Z\"}",
"{\"timestamp\": \"2023-04-01 14:30:00\"}"
};
for (String testDate : testDates) {
try {
Event parsedEvent = advancedGson.fromJson(testDate, Event.class);
System.out.println("Successfully parsed: " + parsedEvent.timestamp.toString());
} catch (JsonParseException e) {
System.err.println("Failed to parse: " + testDate + " Error: " + e.getMessage());
}
}
}
}
Testing Different Scenarios
public class JodaDateTimeAdapterTest {
private Gson gson;
private AdvancedJodaDateTimeAdapter adapter;
@Before
public void setUp() {
adapter = new AdvancedJodaDateTimeAdapter();
gson = new GsonBuilder()
.registerTypeAdapter(DateTime.class, adapter)
.create();
}
@Test
public void testSerializeDateTime() {
DateTime now = new DateTime();
String json = gson.toJson(now);
DateTime parsed = gson.fromJson(json, DateTime.class);
assertEquals(now, parsed);
}
@Test
public void testDeserializeMultipleFormats() {
// Test cases
String[] validDateTimes = {
"2023-04-01T14:30:00.000Z", // ISO format with milliseconds
"2023-04-01T14:30:00Z", // ISO format without milliseconds
"2023-04-01 14:30:00" // Simple format
};
for (String dateTimeStr : validDateTimes) {
DateTime parsed = gson.fromJson("\"" + dateTimeStr + "\"", DateTime.class);
assertNotNull("Failed to parse: " + dateTimeStr, parsed);
}
}
@Test(expected = JsonParseException.class)
public void testInvalidFormat() {
gson.fromJson("\"invalid-date-format\"", DateTime.class);
}
}
Configuration with Spring Boot
We can configure the Gson bean with the custom adapter:
@Configuration
public class GsonConfig {
@Bean
public Gson gson() {
return new GsonBuilder()
.registerTypeAdapter(DateTime.class, new AdvancedJodaDateTimeAdapter())
.setPrettyPrinting()
.create();
}
}
Key Features of this Implementation:
Multiple Format Support: The advanced adapter can handle various datetime formats.
Error Handling: Proper exception handling with meaningful error messages.
Thread Safety: The implementation is thread-safe.
Flexible Configuration: Can be customized with different formatters.
Null Safety: Handles null values appropriately.
Last updated