# 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

```java
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

```java
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

```java
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

```java
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:

```java
@Configuration
public class GsonConfig {

    @Bean
    public Gson gson() {
        return new GsonBuilder()
            .registerTypeAdapter(DateTime.class, new AdvancedJodaDateTimeAdapter())
            .setPrettyPrinting()
            .create();
    }
}
```

### Key Features of this Implementation:

1. **Multiple Format Support**: The advanced adapter can handle various datetime formats.
2. **Error Handling**: Proper exception handling with meaningful error messages.
3. **Thread Safety**: The implementation is thread-safe.
4. **Flexible Configuration**: Can be customized with different formatters.
5. **Null Safety**: Handles null values appropriately.
