Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplify custom adapters #280

Merged
merged 5 commits into from
Aug 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading