From b1ae308d4901e09ca37431727934986d4f082ab0 Mon Sep 17 00:00:00 2001 From: Josiah Noel <32279667+SentryMan@users.noreply.github.com> Date: Sat, 3 Aug 2024 10:34:24 -0400 Subject: [PATCH 1/5] simplify generic custom adapters --- .../java/io/avaje/jsonb/generator/JsonbProcessor.java | 7 ++++++- jsonb/src/main/java/io/avaje/jsonb/CustomAdapter.java | 11 ++--------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/jsonb-generator/src/main/java/io/avaje/jsonb/generator/JsonbProcessor.java b/jsonb-generator/src/main/java/io/avaje/jsonb/generator/JsonbProcessor.java index d19af03e..9adbf7a1 100644 --- a/jsonb-generator/src/main/java/io/avaje/jsonb/generator/JsonbProcessor.java +++ b/jsonb-generator/src/main/java/io/avaje/jsonb/generator/JsonbProcessor.java @@ -15,6 +15,7 @@ import io.avaje.prism.GenerateAPContext; import io.avaje.prism.GenerateModuleInfoReader; +import io.avaje.prism.GenerateUtils; import static java.util.stream.Collectors.joining; @@ -25,6 +26,7 @@ import java.util.function.Predicate; import java.util.stream.Stream; +@GenerateUtils @GenerateAPContext @GenerateModuleInfoReader @SupportedAnnotationTypes({ @@ -112,7 +114,10 @@ private Optional extends Set extends Element>> getElements(RoundEnvironment private void registerCustomAdapters(Set extends Element> elements) { for (final var typeElement : ElementFilter.typesIn(elements)) { final var type = typeElement.getQualifiedName().toString(); - if (CustomAdapterPrism.getInstanceOn(typeElement).isGeneric()) { + if (typeElement.getInterfaces().stream() + .map(UType::parse) + .filter(u -> u.full().contains("JsonAdapter")) + .anyMatch(u -> u.param0().isGeneric())) { ElementFilter.fieldsIn(typeElement.getEnclosedElements()).stream() .filter(isStaticFactory()) .findFirst() diff --git a/jsonb/src/main/java/io/avaje/jsonb/CustomAdapter.java b/jsonb/src/main/java/io/avaje/jsonb/CustomAdapter.java index 2ab72d35..9f9e3224 100644 --- a/jsonb/src/main/java/io/avaje/jsonb/CustomAdapter.java +++ b/jsonb/src/main/java/io/avaje/jsonb/CustomAdapter.java @@ -6,7 +6,6 @@ import java.lang.annotation.Retention; import java.lang.annotation.Target; - /** * Marks a type as a basic user-provided JsonAdapter to be registered automatically. * @@ -35,7 +34,7 @@ * *
{@code * - * @CustomAdapter(isGeneric=true) + * @CustomAdapter * public class CustomJsonAdapterimplements JsonAdapter > { * * private final JsonAdapter genericTypeAdapter; @@ -60,10 +59,4 @@ */ @Target(TYPE) @Retention(SOURCE) -public @interface CustomAdapter { - - /** - * Set to true when the adapter is for a type that uses generics. - */ - boolean isGeneric() default false; -} +public @interface CustomAdapter {} \ No newline at end of file From 9d60d9b77b7a09d7a36ff15ecd003ab91ec410a3 Mon Sep 17 00:00:00 2001 From: Josiah Noel <32279667+SentryMan@users.noreply.github.com> Date: Sat, 3 Aug 2024 10:37:24 -0400 Subject: [PATCH 2/5] Update CustomEntryJsonAdapter.java --- .../java/org/example/other/custom/CustomEntryJsonAdapter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blackbox-test/src/main/java/org/example/other/custom/CustomEntryJsonAdapter.java b/blackbox-test/src/main/java/org/example/other/custom/CustomEntryJsonAdapter.java index 190bfe83..a02ec89f 100644 --- a/blackbox-test/src/main/java/org/example/other/custom/CustomEntryJsonAdapter.java +++ b/blackbox-test/src/main/java/org/example/other/custom/CustomEntryJsonAdapter.java @@ -13,7 +13,7 @@ import io.avaje.jsonb.Types; import io.avaje.jsonb.spi.PropertyNames; -@CustomAdapter(isGeneric = true) +@CustomAdapter public class CustomEntryJsonAdapter implements JsonAdapter > { private final JsonAdapter generic1; From c117e1a692053e8b0915cfe097186cbdc949c6fa Mon Sep 17 00:00:00 2001 From: Josiah Noel <32279667+SentryMan@users.noreply.github.com> Date: Sat, 3 Aug 2024 10:39:23 -0400 Subject: [PATCH 3/5] fix tests --- .../java/org/example/other/custom/CustomEntryJsonAdapter.java | 1 - .../jsonb/generator/models/valid/CustomEntryJsonAdapter.java | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/blackbox-test/src/main/java/org/example/other/custom/CustomEntryJsonAdapter.java b/blackbox-test/src/main/java/org/example/other/custom/CustomEntryJsonAdapter.java index a02ec89f..acce5d2b 100644 --- a/blackbox-test/src/main/java/org/example/other/custom/CustomEntryJsonAdapter.java +++ b/blackbox-test/src/main/java/org/example/other/custom/CustomEntryJsonAdapter.java @@ -1,6 +1,5 @@ package org.example.other.custom; -import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.AbstractMap.SimpleImmutableEntry; import java.util.Map.Entry; diff --git a/jsonb-generator/src/test/java/io/avaje/jsonb/generator/models/valid/CustomEntryJsonAdapter.java b/jsonb-generator/src/test/java/io/avaje/jsonb/generator/models/valid/CustomEntryJsonAdapter.java index c24ebf55..07ec6d9f 100644 --- a/jsonb-generator/src/test/java/io/avaje/jsonb/generator/models/valid/CustomEntryJsonAdapter.java +++ b/jsonb-generator/src/test/java/io/avaje/jsonb/generator/models/valid/CustomEntryJsonAdapter.java @@ -12,7 +12,7 @@ import io.avaje.jsonb.Types; import io.avaje.jsonb.spi.PropertyNames; -@CustomAdapter(isGeneric = true) +@CustomAdapter public class CustomEntryJsonAdapter implements JsonAdapter > { private final JsonAdapter generic1; From c3f217dc466b621d9228ea2524f6ef80224014a3 Mon Sep 17 00:00:00 2001 From: Josiah Noel <32279667+SentryMan@users.noreply.github.com> Date: Sat, 3 Aug 2024 15:20:03 -0400 Subject: [PATCH 4/5] allow simple custom adapters --- .../io/avaje/jsonb/generator/JsonbProcessor.java | 2 +- .../generator/models/valid/CustomJsonAdapter.java | 3 +-- jsonb/src/main/java/io/avaje/jsonb/Jsonb.java | 6 ++++++ jsonb/src/main/java/io/avaje/jsonb/core/DJsonb.java | 12 ++++++++++++ 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/jsonb-generator/src/main/java/io/avaje/jsonb/generator/JsonbProcessor.java b/jsonb-generator/src/main/java/io/avaje/jsonb/generator/JsonbProcessor.java index 9adbf7a1..025c73db 100644 --- a/jsonb-generator/src/main/java/io/avaje/jsonb/generator/JsonbProcessor.java +++ b/jsonb-generator/src/main/java/io/avaje/jsonb/generator/JsonbProcessor.java @@ -136,7 +136,7 @@ private void registerCustomAdapters(Set extends Element> elements) { .findAny() .ifPresentOrElse( x -> {}, - () -> logError(typeElement, "Non-Generic adapters must have a public constructor with a single Jsonb parameter")); + () -> logNote(typeElement, "Non-Generic adapters should have a public constructor with a single Jsonb parameter")); metaData.add(type); } diff --git a/jsonb-generator/src/test/java/io/avaje/jsonb/generator/models/valid/CustomJsonAdapter.java b/jsonb-generator/src/test/java/io/avaje/jsonb/generator/models/valid/CustomJsonAdapter.java index 14df6f56..136fee99 100644 --- a/jsonb-generator/src/test/java/io/avaje/jsonb/generator/models/valid/CustomJsonAdapter.java +++ b/jsonb-generator/src/test/java/io/avaje/jsonb/generator/models/valid/CustomJsonAdapter.java @@ -4,13 +4,12 @@ import io.avaje.jsonb.JsonAdapter; import io.avaje.jsonb.JsonReader; import io.avaje.jsonb.JsonWriter; -import io.avaje.jsonb.Jsonb; import io.avaje.jsonb.generator.models.valid.Example3Packet.Example2Packet; @CustomAdapter public class CustomJsonAdapter implements JsonAdapter { - public CustomJsonAdapter(Jsonb jsonb) {} +// public CustomJsonAdapter(Jsonb jsonb) {} @Override public void toJson(JsonWriter writer, Example2Packet value) {} diff --git a/jsonb/src/main/java/io/avaje/jsonb/Jsonb.java b/jsonb/src/main/java/io/avaje/jsonb/Jsonb.java index 7dc81369..316bb100 100644 --- a/jsonb/src/main/java/io/avaje/jsonb/Jsonb.java +++ b/jsonb/src/main/java/io/avaje/jsonb/Jsonb.java @@ -13,6 +13,7 @@ import java.io.Reader; import java.io.Writer; import java.lang.reflect.Type; +import java.util.function.Supplier; /** * Provides access to json adapters by type. @@ -386,6 +387,11 @@ interface Builder { */ Builder add(Type type, JsonAdapter jsonAdapter); + /** + * Add a Supplier which provides a JsonAdapter to use for the given type. + */ + Builder add(Type type, Supplier > builder); + /** * Add a AdapterBuilder which provides a JsonAdapter to use for the given type. */ diff --git a/jsonb/src/main/java/io/avaje/jsonb/core/DJsonb.java b/jsonb/src/main/java/io/avaje/jsonb/core/DJsonb.java index 1d8174ce..9b0a258c 100644 --- a/jsonb/src/main/java/io/avaje/jsonb/core/DJsonb.java +++ b/jsonb/src/main/java/io/avaje/jsonb/core/DJsonb.java @@ -13,6 +13,7 @@ import java.lang.reflect.Type; import java.util.*; import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Supplier; import static io.avaje.jsonb.core.Util.*; import static java.util.Objects.requireNonNull; @@ -276,6 +277,11 @@ public Builder add(Type type, JsonAdapter jsonAdapter) { return add(newAdapterFactory(type, jsonAdapter)); } + @Override + public Builder add(Type type, Supplier > jsonAdapter) { + return add(newAdapterFactory(type, jsonAdapter)); + } + @Override public Builder add(JsonbComponent component) { component.register(this); @@ -317,6 +323,12 @@ static JsonAdapter.Factory newAdapterFactory(Type type, JsonAdapter jsonA return (targetType, jsonb) -> simpleMatch(type, targetType) ? jsonAdapter : null; } + static JsonAdapter.Factory newAdapterFactory(Type type, Supplier > jsonAdapter) { + requireNonNull(type); + requireNonNull(jsonAdapter); + return (targetType, jsonb) -> simpleMatch(type, targetType) ? jsonAdapter.get() : null; + } + static JsonAdapter.Factory newAdapterFactory(Type type, AdapterBuilder builder) { requireNonNull(type); requireNonNull(builder); From 778d220dd527e59561f4921100224317022c29f7 Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Mon, 5 Aug 2024 21:42:46 +1200 Subject: [PATCH 5/5] Format, extract method, add missing test --- .../customtype/CustomScalarTypeTest.java | 18 ++++++++++++++++++ .../avaje/jsonb/generator/JsonbProcessor.java | 12 ++++++++---- .../models/valid/CustomJsonAdapter.java | 2 -- .../java/io/avaje/jsonb/CustomAdapter.java | 2 +- .../main/java/io/avaje/jsonb/core/DJsonb.java | 9 +-------- 5 files changed, 28 insertions(+), 15 deletions(-) diff --git a/blackbox-test/src/test/java/org/example/customer/customtype/CustomScalarTypeTest.java b/blackbox-test/src/test/java/org/example/customer/customtype/CustomScalarTypeTest.java index 4ff00988..61260488 100644 --- a/blackbox-test/src/test/java/org/example/customer/customtype/CustomScalarTypeTest.java +++ b/blackbox-test/src/test/java/org/example/customer/customtype/CustomScalarTypeTest.java @@ -50,6 +50,24 @@ void toJson_fromJson() { assertThat(wrapper1.custom()).isEqualTo(wrapper.custom()); } + @Test + void toJson_fromJson_usingSupplier() { + Jsonb jsonb = Jsonb.builder() + // register a supplier + .add(MyCustomScalarType.class, () -> new CustomTypeAdapter().nullSafe()) + .build(); + + MyWrapper wrapper = new MyWrapper(42, "hello", new MyCustomScalarType("hello".getBytes(StandardCharsets.UTF_8))); + + String asJson = jsonb.toJson(wrapper); + assertThat(asJson).isEqualTo("{\"id\":42,\"base\":\"hello\",\"custom\":\"aGVsbG8=\"}"); + + MyWrapper wrapper1 = jsonb.type(MyWrapper.class).fromJson(asJson); + + assertThat(wrapper1).isEqualTo(wrapper); + assertThat(wrapper1.custom()).isEqualTo(wrapper.custom()); + } + static class CustomTypeAdapter implements JsonAdapter { @Override diff --git a/jsonb-generator/src/main/java/io/avaje/jsonb/generator/JsonbProcessor.java b/jsonb-generator/src/main/java/io/avaje/jsonb/generator/JsonbProcessor.java index 025c73db..a200e417 100644 --- a/jsonb-generator/src/main/java/io/avaje/jsonb/generator/JsonbProcessor.java +++ b/jsonb-generator/src/main/java/io/avaje/jsonb/generator/JsonbProcessor.java @@ -114,10 +114,7 @@ private Optional extends Set extends Element>> getElements(RoundEnvironment private void registerCustomAdapters(Set extends Element> elements) { for (final var typeElement : ElementFilter.typesIn(elements)) { final var type = typeElement.getQualifiedName().toString(); - if (typeElement.getInterfaces().stream() - .map(UType::parse) - .filter(u -> u.full().contains("JsonAdapter")) - .anyMatch(u -> u.param0().isGeneric())) { + if (isGenericJsonAdapter(typeElement)) { ElementFilter.fieldsIn(typeElement.getEnclosedElements()).stream() .filter(isStaticFactory()) .findFirst() @@ -143,6 +140,13 @@ private void registerCustomAdapters(Set extends Element> elements) { } } + private static boolean isGenericJsonAdapter(TypeElement typeElement) { + return typeElement.getInterfaces().stream() + .map(UType::parse) + .filter(u -> u.full().contains("JsonAdapter")) + .anyMatch(u -> u.param0().isGeneric()); + } + private static Predicate isStaticFactory() { return v -> v.getModifiers().contains(Modifier.STATIC) && "FACTORY".equals(v.getSimpleName().toString()); } diff --git a/jsonb-generator/src/test/java/io/avaje/jsonb/generator/models/valid/CustomJsonAdapter.java b/jsonb-generator/src/test/java/io/avaje/jsonb/generator/models/valid/CustomJsonAdapter.java index 136fee99..f28beeae 100644 --- a/jsonb-generator/src/test/java/io/avaje/jsonb/generator/models/valid/CustomJsonAdapter.java +++ b/jsonb-generator/src/test/java/io/avaje/jsonb/generator/models/valid/CustomJsonAdapter.java @@ -9,8 +9,6 @@ @CustomAdapter public class CustomJsonAdapter implements JsonAdapter { -// public CustomJsonAdapter(Jsonb jsonb) {} - @Override public void toJson(JsonWriter writer, Example2Packet value) {} diff --git a/jsonb/src/main/java/io/avaje/jsonb/CustomAdapter.java b/jsonb/src/main/java/io/avaje/jsonb/CustomAdapter.java index 9f9e3224..4ac66767 100644 --- a/jsonb/src/main/java/io/avaje/jsonb/CustomAdapter.java +++ b/jsonb/src/main/java/io/avaje/jsonb/CustomAdapter.java @@ -59,4 +59,4 @@ */ @Target(TYPE) @Retention(SOURCE) -public @interface CustomAdapter {} \ No newline at end of file +public @interface CustomAdapter {} diff --git a/jsonb/src/main/java/io/avaje/jsonb/core/DJsonb.java b/jsonb/src/main/java/io/avaje/jsonb/core/DJsonb.java index 9b0a258c..8970a572 100644 --- a/jsonb/src/main/java/io/avaje/jsonb/core/DJsonb.java +++ b/jsonb/src/main/java/io/avaje/jsonb/core/DJsonb.java @@ -307,14 +307,7 @@ private void registerComponents() { @Override public DJsonb build() { registerComponents(); - return new DJsonb( - adapter, - factories, - serializeNulls, - serializeEmpty, - failOnUnknown, - mathTypesAsString, - strategy); + return new DJsonb(adapter, factories, serializeNulls, serializeEmpty, failOnUnknown, mathTypesAsString, strategy); } static JsonAdapter.Factory newAdapterFactory(Type type, JsonAdapter jsonAdapter) {