Skip to content

Commit

Permalink
Simplify custom adapters (#280)
Browse files Browse the repository at this point in the history
* simplify generic custom adapters

* Update CustomEntryJsonAdapter.java

* fix tests

* allow simple custom adapters

* Format, extract method, add missing test

---------

Co-authored-by: Rob Bygrave <[email protected]>
  • Loading branch information
SentryMan and rbygrave authored Aug 5, 2024
1 parent 131e17e commit f46f7bc
Show file tree
Hide file tree
Showing 8 changed files with 52 additions and 25 deletions.
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -13,7 +12,7 @@
import io.avaje.jsonb.Types;
import io.avaje.jsonb.spi.PropertyNames;

@CustomAdapter(isGeneric = true)
@CustomAdapter
public class CustomEntryJsonAdapter<K, V> implements JsonAdapter<Entry<K, V>> {

private final JsonAdapter<K> generic1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<MyCustomScalarType> {

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -25,6 +26,7 @@
import java.util.function.Predicate;
import java.util.stream.Stream;

@GenerateUtils
@GenerateAPContext
@GenerateModuleInfoReader
@SupportedAnnotationTypes({
Expand Down Expand Up @@ -112,7 +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 (CustomAdapterPrism.getInstanceOn(typeElement).isGeneric()) {
if (isGenericJsonAdapter(typeElement)) {
ElementFilter.fieldsIn(typeElement.getEnclosedElements()).stream()
.filter(isStaticFactory())
.findFirst()
Expand All @@ -131,13 +133,20 @@ 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);
}
}
}

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<VariableElement> isStaticFactory() {
return v -> v.getModifiers().contains(Modifier.STATIC) && "FACTORY".equals(v.getSimpleName().toString());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import io.avaje.jsonb.Types;
import io.avaje.jsonb.spi.PropertyNames;

@CustomAdapter(isGeneric = true)
@CustomAdapter
public class CustomEntryJsonAdapter<K, V> implements JsonAdapter<Entry<K, V>> {

private final JsonAdapter<K> generic1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,11 @@
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<Example2Packet> {

public CustomJsonAdapter(Jsonb jsonb) {}

@Override
public void toJson(JsonWriter writer, Example2Packet value) {}

Expand Down
11 changes: 2 additions & 9 deletions jsonb/src/main/java/io/avaje/jsonb/CustomAdapter.java
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down Expand Up @@ -35,7 +34,7 @@
*
* <pre>{@code
*
* @CustomAdapter(isGeneric=true)
* @CustomAdapter
* public class CustomJsonAdapter<T> implements JsonAdapter<GenericType<T>> {
*
* private final JsonAdapter<T> genericTypeAdapter;
Expand All @@ -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 {}
6 changes: 6 additions & 0 deletions jsonb/src/main/java/io/avaje/jsonb/Jsonb.java
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -386,6 +387,11 @@ interface Builder {
*/
<T> Builder add(Type type, JsonAdapter<T> jsonAdapter);

/**
* Add a Supplier which provides a JsonAdapter to use for the given type.
*/
<T> Builder add(Type type, Supplier<JsonAdapter<T>> builder);

/**
* Add a AdapterBuilder which provides a JsonAdapter to use for the given type.
*/
Expand Down
21 changes: 13 additions & 8 deletions jsonb/src/main/java/io/avaje/jsonb/core/DJsonb.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -276,6 +277,11 @@ public <T> Builder add(Type type, JsonAdapter<T> jsonAdapter) {
return add(newAdapterFactory(type, jsonAdapter));
}

@Override
public <T> Builder add(Type type, Supplier<JsonAdapter<T>> jsonAdapter) {
return add(newAdapterFactory(type, jsonAdapter));
}

@Override
public Builder add(JsonbComponent component) {
component.register(this);
Expand All @@ -301,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 <T> JsonAdapter.Factory newAdapterFactory(Type type, JsonAdapter<T> jsonAdapter) {
Expand All @@ -317,6 +316,12 @@ static <T> JsonAdapter.Factory newAdapterFactory(Type type, JsonAdapter<T> jsonA
return (targetType, jsonb) -> simpleMatch(type, targetType) ? jsonAdapter : null;
}

static <T> JsonAdapter.Factory newAdapterFactory(Type type, Supplier<JsonAdapter<T>> 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);
Expand Down

0 comments on commit f46f7bc

Please sign in to comment.