Skip to content

Commit f46f7bc

Browse files
SentryManrbygrave
andauthored
Simplify custom adapters (#280)
* simplify generic custom adapters * Update CustomEntryJsonAdapter.java * fix tests * allow simple custom adapters * Format, extract method, add missing test --------- Co-authored-by: Rob Bygrave <robin.bygrave@gmail.com>
1 parent 131e17e commit f46f7bc

File tree

8 files changed

+52
-25
lines changed

8 files changed

+52
-25
lines changed

blackbox-test/src/main/java/org/example/other/custom/CustomEntryJsonAdapter.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package org.example.other.custom;
22

3-
import java.lang.reflect.ParameterizedType;
43
import java.lang.reflect.Type;
54
import java.util.AbstractMap.SimpleImmutableEntry;
65
import java.util.Map.Entry;
@@ -13,7 +12,7 @@
1312
import io.avaje.jsonb.Types;
1413
import io.avaje.jsonb.spi.PropertyNames;
1514

16-
@CustomAdapter(isGeneric = true)
15+
@CustomAdapter
1716
public class CustomEntryJsonAdapter<K, V> implements JsonAdapter<Entry<K, V>> {
1817

1918
private final JsonAdapter<K> generic1;

blackbox-test/src/test/java/org/example/customer/customtype/CustomScalarTypeTest.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,24 @@ void toJson_fromJson() {
5050
assertThat(wrapper1.custom()).isEqualTo(wrapper.custom());
5151
}
5252

53+
@Test
54+
void toJson_fromJson_usingSupplier() {
55+
Jsonb jsonb = Jsonb.builder()
56+
// register a supplier
57+
.add(MyCustomScalarType.class, () -> new CustomTypeAdapter().nullSafe())
58+
.build();
59+
60+
MyWrapper wrapper = new MyWrapper(42, "hello", new MyCustomScalarType("hello".getBytes(StandardCharsets.UTF_8)));
61+
62+
String asJson = jsonb.toJson(wrapper);
63+
assertThat(asJson).isEqualTo("{\"id\":42,\"base\":\"hello\",\"custom\":\"aGVsbG8=\"}");
64+
65+
MyWrapper wrapper1 = jsonb.type(MyWrapper.class).fromJson(asJson);
66+
67+
assertThat(wrapper1).isEqualTo(wrapper);
68+
assertThat(wrapper1.custom()).isEqualTo(wrapper.custom());
69+
}
70+
5371
static class CustomTypeAdapter implements JsonAdapter<MyCustomScalarType> {
5472

5573
@Override

jsonb-generator/src/main/java/io/avaje/jsonb/generator/JsonbProcessor.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
import io.avaje.prism.GenerateAPContext;
1717
import io.avaje.prism.GenerateModuleInfoReader;
18+
import io.avaje.prism.GenerateUtils;
1819

1920
import static java.util.stream.Collectors.joining;
2021

@@ -25,6 +26,7 @@
2526
import java.util.function.Predicate;
2627
import java.util.stream.Stream;
2728

29+
@GenerateUtils
2830
@GenerateAPContext
2931
@GenerateModuleInfoReader
3032
@SupportedAnnotationTypes({
@@ -112,7 +114,7 @@ private Optional<? extends Set<? extends Element>> getElements(RoundEnvironment
112114
private void registerCustomAdapters(Set<? extends Element> elements) {
113115
for (final var typeElement : ElementFilter.typesIn(elements)) {
114116
final var type = typeElement.getQualifiedName().toString();
115-
if (CustomAdapterPrism.getInstanceOn(typeElement).isGeneric()) {
117+
if (isGenericJsonAdapter(typeElement)) {
116118
ElementFilter.fieldsIn(typeElement.getEnclosedElements()).stream()
117119
.filter(isStaticFactory())
118120
.findFirst()
@@ -131,13 +133,20 @@ private void registerCustomAdapters(Set<? extends Element> elements) {
131133
.findAny()
132134
.ifPresentOrElse(
133135
x -> {},
134-
() -> logError(typeElement, "Non-Generic adapters must have a public constructor with a single Jsonb parameter"));
136+
() -> logNote(typeElement, "Non-Generic adapters should have a public constructor with a single Jsonb parameter"));
135137

136138
metaData.add(type);
137139
}
138140
}
139141
}
140142

143+
private static boolean isGenericJsonAdapter(TypeElement typeElement) {
144+
return typeElement.getInterfaces().stream()
145+
.map(UType::parse)
146+
.filter(u -> u.full().contains("JsonAdapter"))
147+
.anyMatch(u -> u.param0().isGeneric());
148+
}
149+
141150
private static Predicate<VariableElement> isStaticFactory() {
142151
return v -> v.getModifiers().contains(Modifier.STATIC) && "FACTORY".equals(v.getSimpleName().toString());
143152
}

jsonb-generator/src/test/java/io/avaje/jsonb/generator/models/valid/CustomEntryJsonAdapter.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
import io.avaje.jsonb.Types;
1313
import io.avaje.jsonb.spi.PropertyNames;
1414

15-
@CustomAdapter(isGeneric = true)
15+
@CustomAdapter
1616
public class CustomEntryJsonAdapter<K, V> implements JsonAdapter<Entry<K, V>> {
1717

1818
private final JsonAdapter<K> generic1;

jsonb-generator/src/test/java/io/avaje/jsonb/generator/models/valid/CustomJsonAdapter.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,11 @@
44
import io.avaje.jsonb.JsonAdapter;
55
import io.avaje.jsonb.JsonReader;
66
import io.avaje.jsonb.JsonWriter;
7-
import io.avaje.jsonb.Jsonb;
87
import io.avaje.jsonb.generator.models.valid.Example3Packet.Example2Packet;
98

109
@CustomAdapter
1110
public class CustomJsonAdapter implements JsonAdapter<Example2Packet> {
1211

13-
public CustomJsonAdapter(Jsonb jsonb) {}
14-
1512
@Override
1613
public void toJson(JsonWriter writer, Example2Packet value) {}
1714

jsonb/src/main/java/io/avaje/jsonb/CustomAdapter.java

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import java.lang.annotation.Retention;
77
import java.lang.annotation.Target;
88

9-
109
/**
1110
* Marks a type as a basic user-provided JsonAdapter to be registered automatically.
1211
*
@@ -35,7 +34,7 @@
3534
*
3635
* <pre>{@code
3736
*
38-
* @CustomAdapter(isGeneric=true)
37+
* @CustomAdapter
3938
* public class CustomJsonAdapter<T> implements JsonAdapter<GenericType<T>> {
4039
*
4140
* private final JsonAdapter<T> genericTypeAdapter;
@@ -60,10 +59,4 @@
6059
*/
6160
@Target(TYPE)
6261
@Retention(SOURCE)
63-
public @interface CustomAdapter {
64-
65-
/**
66-
* Set to true when the adapter is for a type that uses generics.
67-
*/
68-
boolean isGeneric() default false;
69-
}
62+
public @interface CustomAdapter {}

jsonb/src/main/java/io/avaje/jsonb/Jsonb.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import java.io.Reader;
1414
import java.io.Writer;
1515
import java.lang.reflect.Type;
16+
import java.util.function.Supplier;
1617

1718
/**
1819
* Provides access to json adapters by type.
@@ -386,6 +387,11 @@ interface Builder {
386387
*/
387388
<T> Builder add(Type type, JsonAdapter<T> jsonAdapter);
388389

390+
/**
391+
* Add a Supplier which provides a JsonAdapter to use for the given type.
392+
*/
393+
<T> Builder add(Type type, Supplier<JsonAdapter<T>> builder);
394+
389395
/**
390396
* Add a AdapterBuilder which provides a JsonAdapter to use for the given type.
391397
*/

jsonb/src/main/java/io/avaje/jsonb/core/DJsonb.java

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import java.lang.reflect.Type;
1414
import java.util.*;
1515
import java.util.concurrent.ConcurrentHashMap;
16+
import java.util.function.Supplier;
1617

1718
import static io.avaje.jsonb.core.Util.*;
1819
import static java.util.Objects.requireNonNull;
@@ -276,6 +277,11 @@ public <T> Builder add(Type type, JsonAdapter<T> jsonAdapter) {
276277
return add(newAdapterFactory(type, jsonAdapter));
277278
}
278279

280+
@Override
281+
public <T> Builder add(Type type, Supplier<JsonAdapter<T>> jsonAdapter) {
282+
return add(newAdapterFactory(type, jsonAdapter));
283+
}
284+
279285
@Override
280286
public Builder add(JsonbComponent component) {
281287
component.register(this);
@@ -301,14 +307,7 @@ private void registerComponents() {
301307
@Override
302308
public DJsonb build() {
303309
registerComponents();
304-
return new DJsonb(
305-
adapter,
306-
factories,
307-
serializeNulls,
308-
serializeEmpty,
309-
failOnUnknown,
310-
mathTypesAsString,
311-
strategy);
310+
return new DJsonb(adapter, factories, serializeNulls, serializeEmpty, failOnUnknown, mathTypesAsString, strategy);
312311
}
313312

314313
static <T> JsonAdapter.Factory newAdapterFactory(Type type, JsonAdapter<T> jsonAdapter) {
@@ -317,6 +316,12 @@ static <T> JsonAdapter.Factory newAdapterFactory(Type type, JsonAdapter<T> jsonA
317316
return (targetType, jsonb) -> simpleMatch(type, targetType) ? jsonAdapter : null;
318317
}
319318

319+
static <T> JsonAdapter.Factory newAdapterFactory(Type type, Supplier<JsonAdapter<T>> jsonAdapter) {
320+
requireNonNull(type);
321+
requireNonNull(jsonAdapter);
322+
return (targetType, jsonb) -> simpleMatch(type, targetType) ? jsonAdapter.get() : null;
323+
}
324+
320325
static JsonAdapter.Factory newAdapterFactory(Type type, AdapterBuilder builder) {
321326
requireNonNull(type);
322327
requireNonNull(builder);

0 commit comments

Comments
 (0)