diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index 89d50f85..e827ebdf 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -18,6 +18,8 @@ NOTE: Jackson 3.x components rely on 2.x annotations; there are no separate #314: Add `JsonInclude.Value` convenience constants (contributed by @runeflobakk) +#316: Make `JsonFormat.Features` java.io.Serializable + (requested by @tiger9800) 2.20 (28-Aug-2025) diff --git a/src/main/java/com/fasterxml/jackson/annotation/JsonFormat.java b/src/main/java/com/fasterxml/jackson/annotation/JsonFormat.java index 44f7b9ee..e85867ad 100644 --- a/src/main/java/com/fasterxml/jackson/annotation/JsonFormat.java +++ b/src/main/java/com/fasterxml/jackson/annotation/JsonFormat.java @@ -381,7 +381,10 @@ public enum Feature { * @since 2.6 */ public static class Features + implements java.io.Serializable // @since 2.21 { + private static final long serialVersionUID = 1L; + private final int _enabled, _disabled; private final static Features EMPTY = new Features(0, 0); diff --git a/src/main/java/com/fasterxml/jackson/annotation/JsonIncludeProperties.java b/src/main/java/com/fasterxml/jackson/annotation/JsonIncludeProperties.java index 9f06856a..cab365bf 100644 --- a/src/main/java/com/fasterxml/jackson/annotation/JsonIncludeProperties.java +++ b/src/main/java/com/fasterxml/jackson/annotation/JsonIncludeProperties.java @@ -47,7 +47,8 @@ * * @since 2.12 */ - public static class Value implements JacksonAnnotationValue, java.io.Serializable + public static class Value implements JacksonAnnotationValue, + java.io.Serializable { private static final long serialVersionUID = 1L; diff --git a/src/test/java/com/fasterxml/jackson/annotation/AnnotationTestUtil.java b/src/test/java/com/fasterxml/jackson/annotation/AnnotationTestUtil.java new file mode 100644 index 00000000..2ed365c6 --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/annotation/AnnotationTestUtil.java @@ -0,0 +1,43 @@ +package com.fasterxml.jackson.annotation; + +import static org.junit.jupiter.api.Assertions.fail; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.UncheckedIOException; + +abstract class AnnotationTestUtil { + /* + /********************************************************** + /* JDK ser/deser + /********************************************************** + */ + + public static byte[] jdkSerialize(Object o) + { + ByteArrayOutputStream bytes = new ByteArrayOutputStream(2000); + try (ObjectOutputStream obOut = new ObjectOutputStream(bytes)) { + obOut.writeObject(o); + obOut.close(); + return bytes.toByteArray(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + @SuppressWarnings("unchecked") + public static T jdkDeserialize(byte[] raw) + { + try (ObjectInputStream objIn = new ObjectInputStream(new ByteArrayInputStream(raw))) { + return (T) objIn.readObject(); + } catch (ClassNotFoundException e) { + fail("Missing class: "+e.getMessage()); + return null; + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } +} diff --git a/src/test/java/com/fasterxml/jackson/annotation/JacksonInjectTest.java b/src/test/java/com/fasterxml/jackson/annotation/JacksonInjectTest.java index 51e0bf9d..b1de9a1a 100644 --- a/src/test/java/com/fasterxml/jackson/annotation/JacksonInjectTest.java +++ b/src/test/java/com/fasterxml/jackson/annotation/JacksonInjectTest.java @@ -5,6 +5,7 @@ import static org.junit.jupiter.api.Assertions.*; public class JacksonInjectTest + extends AnnotationTestUtil { private final static class Bogus { @JacksonInject(value="inject", useInput=OptBoolean.FALSE, @@ -47,6 +48,11 @@ public void testFromAnnotation() throws Exception assertFalse(v.equals(EMPTY)); assertFalse(EMPTY.equals(v)); + // Verify JDK serializability + byte[] b = jdkSerialize(v); + JacksonInject.Value v2 = jdkDeserialize(b); + assertEquals(v, v2); + JacksonInject ann2 = Bogus.class.getField("vanilla").getAnnotation(JacksonInject.class); v = JacksonInject.Value.from(ann2); assertEquals(JacksonInject.Value.construct(null, null, null), v, diff --git a/src/test/java/com/fasterxml/jackson/annotation/FormatTest.java b/src/test/java/com/fasterxml/jackson/annotation/JsonFormatTest.java similarity index 97% rename from src/test/java/com/fasterxml/jackson/annotation/FormatTest.java rename to src/test/java/com/fasterxml/jackson/annotation/JsonFormatTest.java index 68cb4f07..0a3efb58 100644 --- a/src/test/java/com/fasterxml/jackson/annotation/FormatTest.java +++ b/src/test/java/com/fasterxml/jackson/annotation/JsonFormatTest.java @@ -11,7 +11,8 @@ * Tests to verify that it is possibly to merge {@link JsonFormat.Value} * instances for overrides. */ -public class FormatTest +public class JsonFormatTest + extends AnnotationTestUtil { private final JsonFormat.Value EMPTY = JsonFormat.Value.empty(); @@ -71,6 +72,10 @@ public void testToString() { @Test public void testFromAnnotation() { + // Trivial case first: + assertSame(EMPTY, JsonFormat.Value.from(null)); + + // then real one JsonFormat ann = Bogus.class.getAnnotation(JsonFormat.class); JsonFormat.Value v = JsonFormat.Value.from(ann); assertEquals("xyz", v.getPattern()); @@ -78,8 +83,11 @@ public void testFromAnnotation() // note: since it's not valid, should not try access as real thing assertEquals("bogus", v.timeZoneAsString()); - // also: - assertSame(EMPTY, JsonFormat.Value.from(null)); + // [annotations#316]: let's also verify JDK serializability + byte[] b = jdkSerialize(v); + JsonFormat.Value v2 = jdkDeserialize(b); + + assertEquals(v, v2); } @Test