diff --git a/jackson-modules/jackson-conversions-2/src/main/java/com/baeldung/jackson/booleanAsInt/Game.java b/jackson-modules/jackson-conversions-2/src/main/java/com/baeldung/jackson/booleanAsInt/Game.java new file mode 100644 index 0000000000..0ad77640d4 --- /dev/null +++ b/jackson-modules/jackson-conversions-2/src/main/java/com/baeldung/jackson/booleanAsInt/Game.java @@ -0,0 +1,49 @@ +package com.baeldung.jackson.booleanAsInt; + +public class Game { + + private Long id; + private String name; + private Boolean paused; + private Boolean over; + + public Game() { + } + + public Game(Long id, String name) { + this.id = id; + this.name = name; + } + + public Long getId() { + return this.id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + + public Boolean isPaused() { + return paused; + } + + public void setPaused(Boolean paused) { + this.paused = paused; + } + + public Boolean isOver() { + return over; + } + + public void setOver(Boolean over) { + this.over = over; + } +} diff --git a/jackson-modules/jackson-conversions-2/src/main/java/com/baeldung/jackson/booleanAsInt/GameAnnotatedByJsonFormat.java b/jackson-modules/jackson-conversions-2/src/main/java/com/baeldung/jackson/booleanAsInt/GameAnnotatedByJsonFormat.java new file mode 100644 index 0000000000..b97625fa6b --- /dev/null +++ b/jackson-modules/jackson-conversions-2/src/main/java/com/baeldung/jackson/booleanAsInt/GameAnnotatedByJsonFormat.java @@ -0,0 +1,56 @@ +package com.baeldung.jackson.booleanAsInt; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonFormat.Shape; + +public class GameAnnotatedByJsonFormat { + + private Long id; + private String name; + + @JsonFormat(shape = Shape.NUMBER) + private boolean paused; + + @JsonFormat(shape = Shape.NUMBER) + private boolean over; + + public GameAnnotatedByJsonFormat() { + } + + public GameAnnotatedByJsonFormat(Long id, String name) { + this.id = id; + this.name = name; + } + + public Long getId() { + return this.id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + + public boolean isPaused() { + return paused; + } + + public void setPaused(boolean paused) { + this.paused = paused; + } + + public boolean isOver() { + return over; + } + + public void setOver(boolean over) { + this.over = over; + } +} diff --git a/jackson-modules/jackson-conversions-2/src/main/java/com/baeldung/jackson/booleanAsInt/GameAnnotatedByJsonSerializeDeserialize.java b/jackson-modules/jackson-conversions-2/src/main/java/com/baeldung/jackson/booleanAsInt/GameAnnotatedByJsonSerializeDeserialize.java new file mode 100644 index 0000000000..50c6d96009 --- /dev/null +++ b/jackson-modules/jackson-conversions-2/src/main/java/com/baeldung/jackson/booleanAsInt/GameAnnotatedByJsonSerializeDeserialize.java @@ -0,0 +1,58 @@ +package com.baeldung.jackson.booleanAsInt; + +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; + +public class GameAnnotatedByJsonSerializeDeserialize { + + private Long id; + private String name; + + @JsonSerialize(using = NumericBooleanSerializer.class) + @JsonDeserialize(using = NumericBooleanDeserializer.class) + private Boolean paused; + + @JsonSerialize(using = NumericBooleanSerializer.class) + @JsonDeserialize(using = NumericBooleanDeserializer.class) + private Boolean over; + + public GameAnnotatedByJsonSerializeDeserialize() { + } + + public GameAnnotatedByJsonSerializeDeserialize(Long id, String name) { + this.id = id; + this.name = name; + } + + public Long getId() { + return this.id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + + public Boolean isPaused() { + return paused; + } + + public void setPaused(Boolean paused) { + this.paused = paused; + } + + public Boolean isOver() { + return over; + } + + public void setOver(Boolean over) { + this.over = over; + } +} diff --git a/jackson-modules/jackson-conversions-2/src/main/java/com/baeldung/jackson/booleanAsInt/NumericBooleanDeserializer.java b/jackson-modules/jackson-conversions-2/src/main/java/com/baeldung/jackson/booleanAsInt/NumericBooleanDeserializer.java new file mode 100644 index 0000000000..e9cb41e91d --- /dev/null +++ b/jackson-modules/jackson-conversions-2/src/main/java/com/baeldung/jackson/booleanAsInt/NumericBooleanDeserializer.java @@ -0,0 +1,23 @@ +package com.baeldung.jackson.booleanAsInt; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import java.io.IOException; + +public class NumericBooleanDeserializer extends JsonDeserializer { + + @Override + public Boolean deserialize(JsonParser p, DeserializationContext ctxt) + throws IOException { + if ("1".equals(p.getText())) { + return Boolean.TRUE; + } + if ("0".equals(p.getText())) { + return Boolean.FALSE; + } + // for other than "1" or "0" throw exception by using Jackson internals + throw ctxt.weirdStringException(p.getText(), Boolean.class, "only \"1\" or \"0\" recognized"); + } + +} diff --git a/jackson-modules/jackson-conversions-2/src/main/java/com/baeldung/jackson/booleanAsInt/NumericBooleanSerializer.java b/jackson-modules/jackson-conversions-2/src/main/java/com/baeldung/jackson/booleanAsInt/NumericBooleanSerializer.java new file mode 100644 index 0000000000..e9f7112b53 --- /dev/null +++ b/jackson-modules/jackson-conversions-2/src/main/java/com/baeldung/jackson/booleanAsInt/NumericBooleanSerializer.java @@ -0,0 +1,15 @@ +package com.baeldung.jackson.booleanAsInt; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import java.io.IOException; + +public class NumericBooleanSerializer extends JsonSerializer { + + @Override + public void serialize(Boolean value, JsonGenerator gen, SerializerProvider serializers) + throws IOException { + gen.writeString(value ? "1" : "0"); + } +} diff --git a/jackson-modules/jackson-conversions-2/src/test/java/com/baeldung/jackson/booleanAsInt/BooleanAsIntegerUnitTest.java b/jackson-modules/jackson-conversions-2/src/test/java/com/baeldung/jackson/booleanAsInt/BooleanAsIntegerUnitTest.java new file mode 100644 index 0000000000..976f3f4915 --- /dev/null +++ b/jackson-modules/jackson-conversions-2/src/test/java/com/baeldung/jackson/booleanAsInt/BooleanAsIntegerUnitTest.java @@ -0,0 +1,132 @@ +package com.baeldung.jackson.booleanAsInt; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonFormat.Shape; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.exc.InvalidFormatException; +import com.fasterxml.jackson.databind.module.SimpleModule; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class BooleanAsIntegerUnitTest { + + private ObjectMapper mapper; + + @BeforeEach + public void setup() { + mapper = new ObjectMapper(); + } + + @Test + public void givenBoolean_serializedAsInteger() throws Exception { + GameAnnotatedByJsonFormat + game = new GameAnnotatedByJsonFormat(1L, "My Game"); + game.setPaused(true); + game.setOver(false); + String json = mapper.writeValueAsString(game); + + assertThat(json) + .isEqualTo("{\"id\":1,\"name\":\"My Game\",\"paused\":1,\"over\":0}"); + } + + @Test + public void givenInteger_deserializedAsBooleanByDefault() throws Exception { + // Integer "1" and "0" values deserialized correctly out of the box. + // No configuration or @JsonFormat annotation needed. + String json = "{\"id\":1,\"name\":\"My Game\",\"paused\":1,\"over\":0}"; + Game game = mapper.readValue(json, Game.class); + + assertThat(game.isPaused()).isEqualTo(true); + assertThat(game.isOver()).isEqualTo(false); + } + + @Test + public void givenBoolean_serializedAsIntegerGlobally() throws Exception { + // global configuration override for the type Boolean + mapper.configOverride(Boolean.class) + .setFormat(JsonFormat.Value.forShape(Shape.NUMBER)); + + Game game = new Game(1L, "My Game"); + game.setPaused(true); + game.setOver(false); + String json = mapper.writeValueAsString(game); + + assertThat(json) + .isEqualTo("{\"id\":1,\"name\":\"My Game\",\"paused\":1,\"over\":0}"); + } + + @Test + public void givenBooleanWithCustomSerializer_serializedAsNumericString() throws Exception { + GameAnnotatedByJsonSerializeDeserialize + game = new GameAnnotatedByJsonSerializeDeserialize(1L, "My Game"); + game.setPaused(true); + game.setOver(false); + String json = mapper.writeValueAsString(game); + + assertThat(json) + .isEqualTo("{\"id\":1,\"name\":\"My Game\",\"paused\":\"1\",\"over\":\"0\"}"); + } + + @Test + public void givenNumericStringWithCustomDeserializer_deserializedAsBoolean() throws Exception { + String json = "{\"id\":1,\"name\":\"My Game\",\"paused\":\"1\",\"over\":\"0\"}"; + GameAnnotatedByJsonSerializeDeserialize + game = mapper.readValue(json, GameAnnotatedByJsonSerializeDeserialize.class); + + assertThat(game.isPaused()).isEqualTo(true); + assertThat(game.isOver()).isEqualTo(false); + } + + @Test + public void givenBooleanWithCustomSerializer_serializedAsNumericStringGlobally() throws Exception { + // setting serializers globally + SimpleModule module = new SimpleModule(); + module.addSerializer(Boolean.class, new NumericBooleanSerializer()); + mapper.registerModule(module); + + Game game = new Game(1L, "My Game"); + game.setPaused(true); + game.setOver(false); + String json = mapper.writeValueAsString(game); + + assertThat(json) + .isEqualTo("{\"id\":1,\"name\":\"My Game\",\"paused\":\"1\",\"over\":\"0\"}"); + } + + @Test + public void givenNumericStringWithCustomDeserializer_deserializedAsBooleanGlobally() throws Exception { + // setting deserializers globally + SimpleModule module = new SimpleModule(); + module.addDeserializer(Boolean.class, new NumericBooleanDeserializer()); + mapper.registerModule(module); + + String json = "{\"id\":1,\"name\":\"My Game\",\"paused\":\"1\",\"over\":\"0\"}"; + Game game = mapper.readValue(json, Game.class); + + assertThat(game.isPaused()).isEqualTo(true); + assertThat(game.isOver()).isEqualTo(false); + } + + @Test + public void givenInvalidStringWithCustomDeserializer_throwsInvalidFormatException() { + // another number other than "1" or "0" + String json = "{\"id\":1,\"name\":\"My Game\",\"paused\":\"5\"}"; + InvalidFormatException e = Assertions.assertThrows( + InvalidFormatException.class, () -> mapper.readValue(json, GameAnnotatedByJsonSerializeDeserialize.class) + ); + + assertThat(e.getValue()).isEqualTo("5"); + + // non-numeric string + String json2 = "{\"id\":1,\"name\":\"My Game\",\"paused\":\"xxx\"}"; + InvalidFormatException e2 = Assertions.assertThrows( + InvalidFormatException.class, () -> mapper.readValue(json2, GameAnnotatedByJsonSerializeDeserialize.class) + ); + + assertThat(e2.getValue()).isEqualTo("xxx"); + } + +}