Skip to content

Commit

Permalink
Support custom serdes for builtin types (#459)
Browse files Browse the repository at this point in the history
Fixes #312
  • Loading branch information
graemerocher committed May 19, 2023
1 parent b61828c commit a5b4c1b
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 32 deletions.
@@ -0,0 +1,60 @@
package io.micronaut.serde.jackson

import io.micronaut.core.annotation.NonNull
import io.micronaut.core.annotation.Order
import io.micronaut.core.type.Argument
import io.micronaut.core.util.StringUtils
import io.micronaut.serde.Decoder
import io.micronaut.serde.Deserializer
import io.micronaut.serde.Encoder
import io.micronaut.serde.ObjectMapper
import io.micronaut.serde.Serde
import io.micronaut.serde.Serializer
import io.micronaut.serde.annotation.Serdeable
import io.micronaut.test.annotation.MockBean
import io.micronaut.test.extensions.spock.annotation.MicronautTest
import jakarta.inject.Inject
import spock.lang.Specification

@MicronautTest
class CustomStringSerdeSpec extends Specification {

@Inject ObjectMapper objectMapper

void "test custom string encoder"() {
expect:
objectMapper.writeValueAsString(new Test(name: "Fred")) == '{"name":"derF"}'
objectMapper.writeValueAsString(new Test(name: "")) == '{"name":null}'
objectMapper.writeValueAsString(new Test(name: " ")) == '{"name":null}'
objectMapper.readValue('{"name":"derF"}', Test).name == "Fred"

}

@Serdeable
static class Test {
String name
}

@MockBean
Serde<String> stringSerde() {
return new Serde<String>() {
@Override
String deserialize(@NonNull Decoder decoder, @NonNull Deserializer.DecoderContext context, @NonNull Argument<? super String> type) throws IOException {
if (decoder.decodeNull()) {
return null
} else {
return decoder.decodeString().reverse()
}
}

@Override
void serialize(@NonNull Encoder encoder, @NonNull Serializer.EncoderContext context, @NonNull Argument<? extends String> type, @NonNull String value) throws IOException {
if (StringUtils.isEmpty(value.trim())) {
encoder.encodeNull()
} else {
encoder.encodeString(value.reverse())
}
}
}
}
}
Expand Up @@ -180,8 +180,16 @@ private void registerPrimitiveSerdes() {
}

private void registerBuiltInSerdes() {
this.deserializerMap.put(new TypeKey(Argument.STRING),
(NullableDeserializer<String>) (decoder, decoderContext, type) -> decoder.decodeString());
if (!this.deserializerDefMap.containsKey(String.class)) {
TypeKey tk = new TypeKey(Argument.STRING);
this.deserializerMap.put(tk,
(NullableDeserializer<String>) (decoder, decoderContext, type) -> decoder.decodeString());
}
if (!this.deserializerDefMap.containsKey(CharSequence.class)) {
TypeKey tk = new TypeKey(Argument.of(CharSequence.class));
this.deserializerMap.put(tk,
(NullableDeserializer<CharSequence>) (decoder, decoderContext, type) -> decoder.decodeString());
}
Stream.of(
new IntegerSerde(),
new LongSerde(),
Expand Down Expand Up @@ -214,8 +222,12 @@ private void registerBuiltInSerdes() {
private void register(SerdeRegistrar<?> serdeRegistrar) {
for (Argument<?> type : serdeRegistrar.getTypes()) {
final TypeKey typeEntry = new TypeKey(type);
DefaultSerdeRegistry.this.deserializerMap.put(typeEntry, serdeRegistrar);
DefaultSerdeRegistry.this.serializerMap.put(typeEntry, serdeRegistrar);
if (!DefaultSerdeRegistry.this.deserializerMap.containsKey(typeEntry)) {
DefaultSerdeRegistry.this.deserializerMap.put(typeEntry, serdeRegistrar);
}
if (!DefaultSerdeRegistry.this.serializerMap.containsKey(typeEntry)) {
DefaultSerdeRegistry.this.serializerMap.put(typeEntry, serdeRegistrar);
}
}
}

Expand Down
Expand Up @@ -48,17 +48,6 @@
@Factory
public class CoreDeserializers {

/**
* Deserializes string types.
*
* @return The string deserializer
*/
@Singleton
@NonNull
protected Deserializer<String> stringDeserializer() {
return new StringDeserializer();
}

/**
* Deserializes array lists.
*
Expand Down Expand Up @@ -189,22 +178,6 @@ protected <V> Deserializer<Optional<V>> optionalDeserializer() {
return new OptionalDeserializer<>();
}

private static class StringDeserializer implements Deserializer<String> {

@Override
public String deserialize(Decoder decoder, DecoderContext decoderContext, Argument<? super String> type) throws IOException {
if (decoder.decodeNull()) {
return null;
}
return decoder.decodeString();
}

@Override
public boolean allowNull() {
return true;
}
}

private static class OptionalDeserializer<V> implements CustomizableDeserializer<Optional<V>> {

@Override
Expand Down
2 changes: 1 addition & 1 deletion settings.gradle.kts
Expand Up @@ -6,7 +6,7 @@ pluginManagement {
}

plugins {
id("io.micronaut.build.shared.settings") version "5.3.16"
id("io.micronaut.build.shared.settings") version "5.4.9"
}

enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
Expand Down

0 comments on commit a5b4c1b

Please sign in to comment.