Skip to content

Commit

Permalink
Implement correct generic arguments matcher (#742)
Browse files Browse the repository at this point in the history
  • Loading branch information
dstepanov authored Jan 30, 2024
1 parent 3b4e8b3 commit 29047c1
Show file tree
Hide file tree
Showing 9 changed files with 742 additions and 231 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -123,4 +123,49 @@ record RecordWithBoxed(Integer value) {}
cleanup:
context.close()
}

void 'test json deserialize a custom container'() {
given:
def context = buildContext('test.SomeModel', """
package test;
import java.io.IOException;
import io.micronaut.core.annotation.NonNull;
import io.micronaut.core.annotation.Nullable;
import io.micronaut.core.type.Argument;
import io.micronaut.serde.Decoder;
import io.micronaut.serde.Deserializer;
import io.micronaut.serde.annotation.Serdeable;
import jakarta.inject.Singleton;
import java.util.LinkedList;
import java.util.List;
record Something(String s) {}
@Serdeable.Deserializable
record SomeModel(List<Something> specificList, List<String> genericList) {}
@Singleton
class ListSomethingDeserializer implements Deserializer<List<Something>> {
@Override
public @Nullable List<Something> deserialize(@NonNull Decoder decoder, @NonNull DecoderContext context, @NonNull Argument<? super List<Something>> type) throws IOException {
var stringValue = decoder.decodeString();
return java.util.Arrays.stream(stringValue.split("\\\\|"))
.map(Something::new)
.toList();
}
}
""")

when:
def result = jsonMapper.readValue("""{
"specificList": "a|b|c",
"genericList": ["a", "b", "c"]
}""", context.getClassLoader().loadClass("test.SomeModel"))
then:
result

cleanup:
context.close()
}
}

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ private abstract static class SpecificOnlyCollectionDeserializer<E, C extends Co

@Override
public Deserializer<C> createSpecific(DecoderContext context, Argument<? super C> type) throws SerdeException {
final Argument[] generics = type.getTypeParameters();
final Argument<?>[] generics = type.getTypeParameters();
if (ArrayUtils.isEmpty(generics)) {
throw new SerdeException("Cannot deserialize raw list");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public class CoreSerdes {

public static final Serde<Duration> DURATION_SERDE = new DurationSerde();
public static final Serde<Period> PERIOD_SERDE = new PeriodSerde();
public static final CharSequenceSerde CHAR_SEQUENCE_SERDE = new CharSequenceSerde();
public static final Serde<CharSequence> CHAR_SEQUENCE_SERDE = new CharSequenceSerde();

/**
* Serde used for object arrays.
Expand Down Expand Up @@ -195,5 +195,10 @@ public CharSequence deserialize(Decoder decoder, DecoderContext decoderContext,
public CharSequence deserializeNullable(@NonNull Decoder decoder, @NonNull DecoderContext context, @NonNull Argument<? super CharSequence> type) throws IOException {
return decoder.decodeStringNullable();
}

@Override
public boolean isEmpty(EncoderContext context, CharSequence value) {
return value == null || value.isEmpty();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,32 +31,6 @@
@Factory
public final class CoreSerializers {

/**
* A serializer for all instances of {@link java.lang.CharSequence}.
*
* @return A char sequence serializer
*/
@Singleton
Serializer<CharSequence> charSequenceSerializer() {
return new Serializer<CharSequence>() {
@Override
public void serialize(Encoder encoder,
EncoderContext context,
Argument<? extends CharSequence> type, CharSequence value) throws IOException {
if (value instanceof String) {
encoder.encodeString((String) value);
} else {
encoder.encodeString(value.toString());
}
}

@Override
public boolean isEmpty(EncoderContext context, CharSequence value) {
return value == null || value.length() == 0;
}
};
}

@Singleton
@Order(1000) // prioritize over character
Serializer<String> stringSerializer() {
Expand All @@ -70,7 +44,7 @@ public void serialize(Encoder encoder,

@Override
public boolean isEmpty(EncoderContext context, String value) {
return value == null || value.length() == 0;
return value == null || value.isEmpty();
}
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,52 +36,42 @@ class OptionalSerializer<T> implements CustomizableSerializer<Optional<T>> {
@SuppressWarnings({"unchecked", "rawtypes"})
@Override
public Serializer<Optional<T>> createSpecific(EncoderContext encoderContext, Argument<? extends Optional<T>> type)
throws SerdeException {
final Argument[] generics = type.getTypeParameters();
throws SerdeException {
final Argument<?>[] generics = type.getTypeParameters();
if (ArrayUtils.isEmpty(generics)) {
throw new SerdeException("Serializing raw optionals is for type: " + type);
}
final Argument<?>[] generics1 = type.getTypeParameters();
if (ArrayUtils.isEmpty(generics1)) {
throw new SerdeException("Serializing raw optionals is not supported for type: " + type);
}
//noinspection unchecked
final Argument<T> generic = (Argument<T>) generics1[0];
final Argument<T> generic = (Argument<T>) generics[0];
final Serializer<? super T> componentSerializer = encoderContext.findSerializer(generic).createSpecific(encoderContext, generic);
return new Serializer<Optional<T>>() {
return new Serializer<>() {

@Override
public void serialize(Encoder encoder, EncoderContext context, Argument<? extends Optional<T>> type, Optional<T> value) throws IOException {
final Argument<?>[] generics1 = type.getTypeParameters();
if (ArrayUtils.isEmpty(generics1)) {
throw new SerdeException("Serializing raw optionals is not supported for type: " + type);
}
//noinspection unchecked
final Argument<T> generic = (Argument<T>) generics1[0];
final T o = value.orElse(null);
if (o != null) {
if (o == null) {
encoder.encodeNull();
} else {
componentSerializer.serialize(
encoder,
context,
generic, o
encoder,
context,
generic,
o
);
} else {
encoder.encodeNull();
}
}

@Override
public boolean isEmpty(EncoderContext context, Optional<T> value) {
Optional o = value;
if (value != null && o.isPresent()) {
return componentSerializer.isEmpty(context, (T) o.get());
if (value != null && value.isPresent()) {
return componentSerializer.isEmpty(context, (T) ((Optional) value).get());
}
return true;
}

@Override
public boolean isAbsent(EncoderContext context, Optional<T> value) {
return value == null || !value.isPresent();
return value == null || value.isEmpty();
}
};
}
Expand Down
Loading

0 comments on commit 29047c1

Please sign in to comment.