|
| 1 | +# lib-serde-gson |
| 2 | + |
| 3 | +Gson-based JSON serialization library implementing the lib-serde `StringEncodingStrategy` interface. |
| 4 | + |
| 5 | +## Installation |
| 6 | + |
| 7 | +```gradle |
| 8 | +dependencies { |
| 9 | + implementation 'io.seqera:lib-serde-gson:1.0.0' |
| 10 | +} |
| 11 | +``` |
| 12 | + |
| 13 | +## Usage |
| 14 | + |
| 15 | +### Basic Usage |
| 16 | + |
| 17 | +```java |
| 18 | +// Create an encoder for your type using anonymous class |
| 19 | +GsonEncodingStrategy<MyData> encoder = new GsonEncodingStrategy<MyData>() {}; |
| 20 | + |
| 21 | +// Encode to JSON |
| 22 | +String json = encoder.encode(myData); |
| 23 | + |
| 24 | +// Decode from JSON |
| 25 | +MyData decoded = encoder.decode(json); |
| 26 | +``` |
| 27 | + |
| 28 | +### Configuration Options |
| 29 | + |
| 30 | +```java |
| 31 | +GsonEncodingStrategy<MyData> encoder = new GsonEncodingStrategy<MyData>() {} |
| 32 | + .withPrettyPrint(true) // Enable pretty printing |
| 33 | + .withSerializeNulls(true); // Include null fields in output |
| 34 | +``` |
| 35 | + |
| 36 | +### Polymorphic Serialization |
| 37 | + |
| 38 | +Use `RuntimeTypeAdapterFactory` for serializing class hierarchies: |
| 39 | + |
| 40 | +```java |
| 41 | +RuntimeTypeAdapterFactory<Animal> factory = RuntimeTypeAdapterFactory |
| 42 | + .of(Animal.class, "@type") |
| 43 | + .registerSubtype(Dog.class, "Dog") |
| 44 | + .registerSubtype(Cat.class, "Cat"); |
| 45 | + |
| 46 | +GsonEncodingStrategy<Animal> encoder = new GsonEncodingStrategy<Animal>() {} |
| 47 | + .withTypeAdapterFactory(factory); |
| 48 | + |
| 49 | +// Serializes to: {"@type":"Dog","name":"Rex","barkVolume":10} |
| 50 | +String json = encoder.encode(new Dog("Rex", 10)); |
| 51 | + |
| 52 | +// Deserializes to correct subtype |
| 53 | +Animal animal = encoder.decode(json); // Returns Dog instance |
| 54 | +``` |
| 55 | + |
| 56 | +## Features |
| 57 | + |
| 58 | +- **Type-safe encoding** with automatic generic type inference |
| 59 | +- **Fluent configuration API** for customizing serialization behavior |
| 60 | +- **Thread-safe** lazy Gson initialization with double-checked locking |
| 61 | +- **Java 8 date/time support** with ISO-8601 format: |
| 62 | + - `Instant` - e.g., `"2025-01-01T00:00:00Z"` |
| 63 | + - `Duration` - e.g., `"PT1H30M"` |
| 64 | + - `OffsetDateTime` - e.g., `"2025-01-01T12:00:00+02:00"` |
| 65 | + - `LocalDateTime` - e.g., `"2025-01-01T12:00:00"` |
| 66 | + - `LocalDate` - e.g., `"2025-01-01"` |
| 67 | + - `LocalTime` - e.g., `"12:30:45"` |
| 68 | +- **Polymorphic type handling** via RuntimeTypeAdapterFactory |
| 69 | +- **Null-safe** encode/decode operations |
| 70 | + |
| 71 | +## Type Adapters |
| 72 | + |
| 73 | +The following type adapters are automatically registered: |
| 74 | + |
| 75 | +| Type | Format | Example | |
| 76 | +|------|--------|---------| |
| 77 | +| `Instant` | ISO-8601 | `"2025-01-06T10:30:00Z"` | |
| 78 | +| `Duration` | ISO-8601 | `"PT2H30M"` | |
| 79 | +| `OffsetDateTime` | ISO-8601 | `"2025-01-06T10:30:00+02:00"` | |
| 80 | +| `LocalDateTime` | ISO-8601 | `"2025-01-06T10:30:00"` | |
| 81 | +| `LocalDate` | ISO-8601 | `"2025-01-06"` | |
| 82 | +| `LocalTime` | ISO-8601 | `"10:30:45"` | |
| 83 | + |
| 84 | +### Custom Type Adapters |
| 85 | + |
| 86 | +You can create custom type adapters for your own types by extending `TypeAdapter<T>`: |
| 87 | + |
| 88 | +```java |
| 89 | +import com.google.gson.TypeAdapter; |
| 90 | +import com.google.gson.stream.JsonReader; |
| 91 | +import com.google.gson.stream.JsonToken; |
| 92 | +import com.google.gson.stream.JsonWriter; |
| 93 | + |
| 94 | +public class MoneyAdapter extends TypeAdapter<Money> { |
| 95 | + @Override |
| 96 | + public void write(JsonWriter writer, Money value) throws IOException { |
| 97 | + if (value == null) { |
| 98 | + writer.nullValue(); |
| 99 | + return; |
| 100 | + } |
| 101 | + // Serialize as "100.50 USD" |
| 102 | + writer.value(value.getAmount() + " " + value.getCurrency()); |
| 103 | + } |
| 104 | + |
| 105 | + @Override |
| 106 | + public Money read(JsonReader reader) throws IOException { |
| 107 | + if (reader.peek() == JsonToken.NULL) { |
| 108 | + reader.nextNull(); |
| 109 | + return null; |
| 110 | + } |
| 111 | + String[] parts = reader.nextString().split(" "); |
| 112 | + return new Money(new BigDecimal(parts[0]), parts[1]); |
| 113 | + } |
| 114 | +} |
| 115 | +``` |
| 116 | + |
| 117 | +Register the custom adapter using a `TypeAdapterFactory`: |
| 118 | + |
| 119 | +```java |
| 120 | +import com.google.gson.Gson; |
| 121 | +import com.google.gson.TypeAdapter; |
| 122 | +import com.google.gson.TypeAdapterFactory; |
| 123 | +import com.google.gson.reflect.TypeToken; |
| 124 | + |
| 125 | +TypeAdapterFactory moneyFactory = new TypeAdapterFactory() { |
| 126 | + @Override |
| 127 | + public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) { |
| 128 | + if (type.getRawType() == Money.class) { |
| 129 | + return (TypeAdapter<T>) new MoneyAdapter(); |
| 130 | + } |
| 131 | + return null; |
| 132 | + } |
| 133 | +}; |
| 134 | + |
| 135 | +GsonEncodingStrategy<Order> encoder = new GsonEncodingStrategy<Order>() {} |
| 136 | + .withTypeAdapterFactory(moneyFactory); |
| 137 | +``` |
| 138 | + |
| 139 | +## Dependencies |
| 140 | + |
| 141 | +- `lib-serde` - Base serialization interfaces |
| 142 | +- `lib-lang` - Type utilities for generic type inference |
| 143 | +- `com.google.code.gson:gson` - Google Gson library |
0 commit comments