Skip to content
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
Expand Up @@ -22,7 +22,7 @@ public FriendlyIdDeserializer() {
this(true);
}

private FriendlyIdDeserializer(boolean useFriendlyFormat) {
FriendlyIdDeserializer(boolean useFriendlyFormat) {
super(UUID.class);
this.useFriendlyFormat = useFriendlyFormat;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import tools.jackson.databind.module.SimpleModule;

import com.devskiller.friendly_id.FriendlyIdFormat;
import com.devskiller.friendly_id.type.FriendlyId;

/**
Expand All @@ -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.
* </p>
* <p>
* 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}.
* </p>
*/
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.
* <p>
* 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public FriendlyIdSerializer() {
this(true);
}

private FriendlyIdSerializer(boolean useFriendlyFormat) {
FriendlyIdSerializer(boolean useFriendlyFormat) {
super(UUID.class);
this.useFriendlyFormat = useFriendlyFormat;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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\"");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,31 @@

import java.util.UUID;

import com.devskiller.friendly_id.FriendlyIdFormat;
import com.devskiller.friendly_id.type.FriendlyId;
import com.fasterxml.jackson.databind.module.SimpleModule;

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());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,23 @@

public class FriendlyIdSerializer extends StdSerializer<UUID> {

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());
}
}
}
Original file line number Diff line number Diff line change
@@ -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\"");
}
}