diff --git a/friendly-id-jackson-datatype/src/main/java/com/devskiller/friendly_id/jackson/FriendlyIdDeserializer.java b/friendly-id-jackson-datatype/src/main/java/com/devskiller/friendly_id/jackson/FriendlyIdDeserializer.java index 1d3cc58..541854e 100644 --- a/friendly-id-jackson-datatype/src/main/java/com/devskiller/friendly_id/jackson/FriendlyIdDeserializer.java +++ b/friendly-id-jackson-datatype/src/main/java/com/devskiller/friendly_id/jackson/FriendlyIdDeserializer.java @@ -22,7 +22,7 @@ public FriendlyIdDeserializer() { this(true); } - private FriendlyIdDeserializer(boolean useFriendlyFormat) { + FriendlyIdDeserializer(boolean useFriendlyFormat) { super(UUID.class); this.useFriendlyFormat = useFriendlyFormat; } diff --git a/friendly-id-jackson-datatype/src/main/java/com/devskiller/friendly_id/jackson/FriendlyIdModule.java b/friendly-id-jackson-datatype/src/main/java/com/devskiller/friendly_id/jackson/FriendlyIdModule.java index 1401403..69fe9e8 100644 --- a/friendly-id-jackson-datatype/src/main/java/com/devskiller/friendly_id/jackson/FriendlyIdModule.java +++ b/friendly-id-jackson-datatype/src/main/java/com/devskiller/friendly_id/jackson/FriendlyIdModule.java @@ -4,6 +4,7 @@ import tools.jackson.databind.module.SimpleModule; +import com.devskiller.friendly_id.FriendlyIdFormat; import com.devskiller.friendly_id.type.FriendlyId; /** @@ -12,14 +13,36 @@ * This module registers custom serializers and deserializers for UUID and FriendlyId types, * enabling automatic conversion between UUID values and their FriendlyId string representation. *

+ *

+ * By default, UUIDs are serialized as Base62 FriendlyIds. Use {@link FriendlyIdFormat#RAW} + * to serialize UUIDs in standard format while still accepting both formats on deserialization. + * Per-field format can always be overridden with {@link com.devskiller.friendly_id.IdFormat @IdFormat}. + *

*/ public class FriendlyIdModule extends SimpleModule { + /** + * Creates a module with default FriendlyId (Base62) serialization format. + */ public FriendlyIdModule() { + this(FriendlyIdFormat.URL62); + } + + /** + * Creates a module with the specified default serialization format. + *

+ * When {@link FriendlyIdFormat#RAW} is used, UUIDs are serialized in standard format + * but deserialization still accepts both standard UUIDs and Base62 FriendlyIds. + * Per-field format can be overridden with {@link com.devskiller.friendly_id.IdFormat @IdFormat}. + * + * @param defaultFormat the default serialization format for UUID fields + */ + public FriendlyIdModule(FriendlyIdFormat defaultFormat) { super("FriendlyIdModule"); + boolean useFriendlyFormat = defaultFormat == FriendlyIdFormat.URL62; - // UUID serializers/deserializers - addSerializer(UUID.class, new FriendlyIdSerializer()); + // UUID serializers/deserializers — deserializer always accepts both formats + addSerializer(UUID.class, new FriendlyIdSerializer(useFriendlyFormat)); addDeserializer(UUID.class, new FriendlyIdDeserializer()); // FriendlyId value object serializers/deserializers diff --git a/friendly-id-jackson-datatype/src/main/java/com/devskiller/friendly_id/jackson/FriendlyIdSerializer.java b/friendly-id-jackson-datatype/src/main/java/com/devskiller/friendly_id/jackson/FriendlyIdSerializer.java index df83a20..308d98f 100644 --- a/friendly-id-jackson-datatype/src/main/java/com/devskiller/friendly_id/jackson/FriendlyIdSerializer.java +++ b/friendly-id-jackson-datatype/src/main/java/com/devskiller/friendly_id/jackson/FriendlyIdSerializer.java @@ -20,7 +20,7 @@ public FriendlyIdSerializer() { this(true); } - private FriendlyIdSerializer(boolean useFriendlyFormat) { + FriendlyIdSerializer(boolean useFriendlyFormat) { super(UUID.class); this.useFriendlyFormat = useFriendlyFormat; } diff --git a/friendly-id-jackson-datatype/src/test/java/com/devskiller/friendly_id/spring/RawFormatModuleTest.java b/friendly-id-jackson-datatype/src/test/java/com/devskiller/friendly_id/spring/RawFormatModuleTest.java new file mode 100644 index 0000000..4756592 --- /dev/null +++ b/friendly-id-jackson-datatype/src/test/java/com/devskiller/friendly_id/spring/RawFormatModuleTest.java @@ -0,0 +1,57 @@ +package com.devskiller.friendly_id.spring; + +import java.util.UUID; + +import org.junit.jupiter.api.Test; +import tools.jackson.databind.json.JsonMapper; + +import com.devskiller.friendly_id.FriendlyIdFormat; +import com.devskiller.friendly_id.FriendlyIds; +import com.devskiller.friendly_id.jackson.FriendlyIdModule; + +import static org.assertj.core.api.Assertions.assertThat; + +class RawFormatModuleTest { + + private final UUID uuid = UUID.fromString("f088ce5b-9279-4cc3-946a-c15ad740dd6d"); + + private final JsonMapper mapper = JsonMapper.builder() + .addModule(new FriendlyIdModule(FriendlyIdFormat.RAW)) + .build(); + + @Test + void shouldSerializeUuidInStandardFormat() { + String json = mapper.writeValueAsString(uuid); + + assertThat(json).isEqualTo("\"f088ce5b-9279-4cc3-946a-c15ad740dd6d\""); + } + + @Test + void shouldDeserializeStandardUuid() { + UUID result = mapper.readValue("\"f088ce5b-9279-4cc3-946a-c15ad740dd6d\"", UUID.class); + + assertThat(result).isEqualTo(uuid); + } + + @Test + void shouldDeserializeFriendlyIdWhenModuleIsRaw() { + String friendlyId = FriendlyIds.toFriendlyId(uuid); + + UUID result = mapper.readValue("\"" + friendlyId + "\"", UUID.class); + + assertThat(result).isEqualTo(uuid); + } + + @Test + void shouldRespectPerFieldOverrideWhenModuleIsRaw() { + var foo = new Foo(uuid, uuid); + + String json = mapper.writeValueAsString(foo); + + // rawUuid has @IdFormat(RAW) -> standard format + assertThat(json).contains("\"rawUuid\":\"f088ce5b-9279-4cc3-946a-c15ad740dd6d\""); + // friendlyId has no annotation, module default is RAW -> standard format + assertThat(json).contains("\"friendlyId\":\"f088ce5b-9279-4cc3-946a-c15ad740dd6d\""); + } + +} diff --git a/friendly-id-jackson2-datatype/src/main/java/com/devskiller/friendly_id/jackson2/FriendlyIdAnnotationIntrospector.java b/friendly-id-jackson2-datatype/src/main/java/com/devskiller/friendly_id/jackson2/FriendlyIdAnnotationIntrospector.java index 6cd8e05..2cbf916 100644 --- a/friendly-id-jackson2-datatype/src/main/java/com/devskiller/friendly_id/jackson2/FriendlyIdAnnotationIntrospector.java +++ b/friendly-id-jackson2-datatype/src/main/java/com/devskiller/friendly_id/jackson2/FriendlyIdAnnotationIntrospector.java @@ -19,30 +19,23 @@ public class FriendlyIdAnnotationIntrospector extends JacksonAnnotationIntrospec @Override public Object findSerializer(Annotated annotatedMethod) { IdFormat annotation = _findAnnotation(annotatedMethod, IdFormat.class); - if (annotatedMethod.getRawType() == UUID.class) { - if (annotation != null) { - return switch (annotation.value()) { - case RAW -> UUIDSerializer.class; - case URL62 -> FriendlyIdSerializer.class; - }; - } - return FriendlyIdSerializer.class; - } else { - return null; + if (annotatedMethod.getRawType() == UUID.class && annotation != null) { + return switch (annotation.value()) { + case RAW -> UUIDSerializer.class; + case URL62 -> FriendlyIdSerializer.class; + }; } + return null; } @Override public Object findDeserializer(Annotated annotatedMethod) { var annotation = _findAnnotation(annotatedMethod, IdFormat.class); - if (rawDeserializationType(annotatedMethod) == UUID.class) { - if (annotation != null) { - return switch (annotation.value()) { - case RAW -> UUIDDeserializer.class; - case URL62 -> FriendlyIdDeserializer.class; - }; - } - return FriendlyIdDeserializer.class; + if (rawDeserializationType(annotatedMethod) == UUID.class && annotation != null) { + return switch (annotation.value()) { + case RAW -> UUIDDeserializer.class; + case URL62 -> FriendlyIdDeserializer.class; + }; } return null; } diff --git a/friendly-id-jackson2-datatype/src/main/java/com/devskiller/friendly_id/jackson2/FriendlyIdJackson2Module.java b/friendly-id-jackson2-datatype/src/main/java/com/devskiller/friendly_id/jackson2/FriendlyIdJackson2Module.java index 3e9fc3b..3ceb5c8 100644 --- a/friendly-id-jackson2-datatype/src/main/java/com/devskiller/friendly_id/jackson2/FriendlyIdJackson2Module.java +++ b/friendly-id-jackson2-datatype/src/main/java/com/devskiller/friendly_id/jackson2/FriendlyIdJackson2Module.java @@ -2,6 +2,7 @@ import java.util.UUID; +import com.devskiller.friendly_id.FriendlyIdFormat; import com.devskiller.friendly_id.type.FriendlyId; import com.fasterxml.jackson.databind.module.SimpleModule; @@ -9,10 +10,23 @@ public class FriendlyIdJackson2Module extends SimpleModule { private final FriendlyIdAnnotationIntrospector introspector; + /** + * Creates a module with default FriendlyId (Base62) serialization format. + */ public FriendlyIdJackson2Module() { + this(FriendlyIdFormat.URL62); + } + + /** + * Creates a module with the specified default serialization format. + * + * @param defaultFormat the default serialization format for UUID fields + */ + public FriendlyIdJackson2Module(FriendlyIdFormat defaultFormat) { + boolean useFriendlyFormat = defaultFormat == FriendlyIdFormat.URL62; introspector = new FriendlyIdAnnotationIntrospector(); addDeserializer(UUID.class, new FriendlyIdDeserializer()); - addSerializer(UUID.class, new FriendlyIdSerializer()); + addSerializer(UUID.class, new FriendlyIdSerializer(useFriendlyFormat)); // Add serializer/deserializer for FriendlyId value object addDeserializer(FriendlyId.class, new FriendlyIdValueDeserializer()); diff --git a/friendly-id-jackson2-datatype/src/main/java/com/devskiller/friendly_id/jackson2/FriendlyIdSerializer.java b/friendly-id-jackson2-datatype/src/main/java/com/devskiller/friendly_id/jackson2/FriendlyIdSerializer.java index 49bae14..472572e 100644 --- a/friendly-id-jackson2-datatype/src/main/java/com/devskiller/friendly_id/jackson2/FriendlyIdSerializer.java +++ b/friendly-id-jackson2-datatype/src/main/java/com/devskiller/friendly_id/jackson2/FriendlyIdSerializer.java @@ -11,12 +11,23 @@ public class FriendlyIdSerializer extends StdSerializer { + private final boolean useFriendlyFormat; + public FriendlyIdSerializer() { + this(true); + } + + FriendlyIdSerializer(boolean useFriendlyFormat) { super(UUID.class); + this.useFriendlyFormat = useFriendlyFormat; } @Override public void serialize(UUID uuid, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { - jsonGenerator.writeString(FriendlyIds.toFriendlyId(uuid)); + if (useFriendlyFormat) { + jsonGenerator.writeString(FriendlyIds.toFriendlyId(uuid)); + } else { + jsonGenerator.writeString(uuid.toString()); + } } } diff --git a/friendly-id-jackson2-datatype/src/test/java/com/devskiller/friendly_id/spring/RawFormatModuleTest.java b/friendly-id-jackson2-datatype/src/test/java/com/devskiller/friendly_id/spring/RawFormatModuleTest.java new file mode 100644 index 0000000..d5e45b1 --- /dev/null +++ b/friendly-id-jackson2-datatype/src/test/java/com/devskiller/friendly_id/spring/RawFormatModuleTest.java @@ -0,0 +1,57 @@ +package com.devskiller.friendly_id.spring; + +import java.util.UUID; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; + +import com.devskiller.friendly_id.FriendlyIdFormat; +import com.devskiller.friendly_id.FriendlyIds; +import com.devskiller.friendly_id.jackson2.FriendlyIdJackson2Module; + +import static org.assertj.core.api.Assertions.assertThat; + +class RawFormatModuleTest { + + private final UUID uuid = UUID.fromString("f088ce5b-9279-4cc3-946a-c15ad740dd6d"); + + private final ObjectMapper mapper = new ObjectMapper() + .registerModule(new FriendlyIdJackson2Module(FriendlyIdFormat.RAW)); + + @Test + void shouldSerializeUuidInStandardFormat() throws Exception { + String json = mapper.writeValueAsString(uuid); + + assertThat(json).isEqualTo("\"f088ce5b-9279-4cc3-946a-c15ad740dd6d\""); + } + + @Test + void shouldDeserializeStandardUuid() throws Exception { + UUID result = mapper.readValue("\"f088ce5b-9279-4cc3-946a-c15ad740dd6d\"", UUID.class); + + assertThat(result).isEqualTo(uuid); + } + + @Test + void shouldDeserializeFriendlyIdWhenModuleIsRaw() throws Exception { + String friendlyId = FriendlyIds.toFriendlyId(uuid); + + UUID result = mapper.readValue("\"" + friendlyId + "\"", UUID.class); + + assertThat(result).isEqualTo(uuid); + } + + @Test + void shouldRespectPerFieldAnnotationWhenModuleIsRaw() throws Exception { + Foo foo = new Foo(); + foo.setRawUuid(uuid); + foo.setFriendlyId(uuid); + + String json = mapper.writeValueAsString(foo); + + // rawUuid has @IdFormat(RAW) -> standard format + assertThat(json).contains("\"rawUuid\":\"f088ce5b-9279-4cc3-946a-c15ad740dd6d\""); + // friendlyId has no annotation, module default is RAW -> standard format + assertThat(json).contains("\"friendlyId\":\"f088ce5b-9279-4cc3-946a-c15ad740dd6d\""); + } +}