Skip to content

Commit ba4adf8

Browse files
committed
feat: Add public GsonTypeMapper exposing gsonBuilder()
1 parent f627eea commit ba4adf8

2 files changed

Lines changed: 139 additions & 0 deletions

File tree

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package com.retailsvc.http;
2+
3+
import com.google.gson.Gson;
4+
import com.google.gson.GsonBuilder;
5+
import com.retailsvc.http.internal.gson.GsonJsonMapper;
6+
import java.util.Objects;
7+
8+
/**
9+
* {@link TypeMapper} for {@code application/json} backed by Gson. Mirrors the ergonomics of {@link
10+
* Jackson2JsonTypeMapper} / {@link Jackson3JsonTypeMapper}: the caller supplies a fully configured
11+
* {@link Gson}; this class never silently mutates it.
12+
*
13+
* <p>The no-argument constructor uses the library's default {@link Gson} — the same JSR-310-aware
14+
* instance the built-in auto-registration produces — making this a drop-in replacement for the
15+
* auto-registered mapper when callers want to wire it explicitly.
16+
*
17+
* <p>To extend the library default with extra type adapters or settings, use {@link
18+
* #gsonBuilder()}:
19+
*
20+
* <pre>{@code
21+
* Gson custom =
22+
* new GsonTypeMapper()
23+
* .gsonBuilder()
24+
* .registerTypeAdapter(MyType.class, new MyTypeAdapter())
25+
* .create();
26+
* new GsonTypeMapper(custom);
27+
* }</pre>
28+
*/
29+
public final class GsonTypeMapper implements TypedTypeMapper {
30+
31+
private final GsonJsonMapper delegate;
32+
33+
/** Creates a mapper backed by the library's default JSR-310-aware {@link Gson}. */
34+
public GsonTypeMapper() {
35+
this.delegate = new GsonJsonMapper();
36+
}
37+
38+
/**
39+
* Creates a mapper backed by the supplied {@link Gson}.
40+
*
41+
* @throws NullPointerException if {@code gson} is null
42+
*/
43+
public GsonTypeMapper(Gson gson) {
44+
this.delegate = new GsonJsonMapper(Objects.requireNonNull(gson, "gson must not be null"));
45+
}
46+
47+
/**
48+
* Returns a {@link GsonBuilder} pre-populated with the wrapped {@link Gson}'s configuration, so
49+
* callers can derive a customized {@link Gson} from the library default (or from their own
50+
* starting point).
51+
*/
52+
public GsonBuilder gsonBuilder() {
53+
return delegate.gson().newBuilder();
54+
}
55+
56+
@Override
57+
public Object readFrom(byte[] body, String contentTypeHeader) {
58+
return delegate.readFrom(body, contentTypeHeader);
59+
}
60+
61+
@Override
62+
public <T> T readAs(byte[] body, String contentTypeHeader, Class<T> type) {
63+
return delegate.readAs(body, contentTypeHeader, type);
64+
}
65+
66+
@Override
67+
public byte[] writeTo(Object value) {
68+
return delegate.writeTo(value);
69+
}
70+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package com.retailsvc.http;
2+
3+
import static org.assertj.core.api.Assertions.assertThat;
4+
import static org.assertj.core.api.Assertions.assertThatNullPointerException;
5+
6+
import com.google.gson.Gson;
7+
import com.google.gson.GsonBuilder;
8+
import java.nio.charset.StandardCharsets;
9+
import java.time.Instant;
10+
import java.util.Collections;
11+
import java.util.Map;
12+
import org.junit.jupiter.api.Test;
13+
14+
class GsonTypeMapperTest {
15+
16+
@Test
17+
void noArgConstructorRoundTripsInstantAsIso8601() {
18+
GsonTypeMapper mapper = new GsonTypeMapper();
19+
20+
byte[] out = mapper.writeTo(Map.of("ts", Instant.parse("2026-05-13T10:00:00Z")));
21+
22+
assertThat(new String(out, StandardCharsets.UTF_8))
23+
.isEqualTo("{\"ts\":\"2026-05-13T10:00:00Z\"}");
24+
}
25+
26+
@Test
27+
void readAsDelegatesToWrappedGson() {
28+
GsonTypeMapper mapper = new GsonTypeMapper();
29+
30+
Item item =
31+
mapper.readAs(
32+
"{\"id\":\"x\",\"qty\":3}".getBytes(StandardCharsets.UTF_8),
33+
"application/json",
34+
Item.class);
35+
36+
assertThat(item.id).isEqualTo("x");
37+
assertThat(item.qty).isEqualTo(3);
38+
}
39+
40+
@Test
41+
void customGsonIsUsed() {
42+
Gson custom = new GsonBuilder().serializeNulls().create();
43+
GsonTypeMapper mapper = new GsonTypeMapper(custom);
44+
45+
assertThat(
46+
new String(mapper.writeTo(Collections.singletonMap("k", null)), StandardCharsets.UTF_8))
47+
.isEqualTo("{\"k\":null}");
48+
}
49+
50+
@Test
51+
void nullGsonRejected() {
52+
assertThatNullPointerException().isThrownBy(() -> new GsonTypeMapper(null));
53+
}
54+
55+
@Test
56+
void gsonBuilderReturnsBuilderForWrappedGson() {
57+
Gson custom = new GsonBuilder().serializeNulls().create();
58+
GsonTypeMapper mapper = new GsonTypeMapper(custom);
59+
60+
Gson derived = mapper.gsonBuilder().create();
61+
62+
assertThat(derived.toJson(Collections.singletonMap("k", null))).isEqualTo("{\"k\":null}");
63+
}
64+
65+
static final class Item {
66+
String id;
67+
int qty;
68+
}
69+
}

0 commit comments

Comments
 (0)