From ebfd1d5bfa9865c3b0456a8c4f9cba3da86f6182 Mon Sep 17 00:00:00 2001 From: Arne Limburg Date: Mon, 29 Jun 2026 19:07:50 +0200 Subject: [PATCH] JOHNZON-431: Add detection of annotations at class level for constructor parameters --- .../apache/johnzon/jsonb/JsonbAccessMode.java | 16 +++++-- .../johnzon/jsonb/JsonbAdapterTest.java | 48 +++++++++++++++++++ 2 files changed, 60 insertions(+), 4 deletions(-) diff --git a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JsonbAccessMode.java b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JsonbAccessMode.java index 4ce308af3..ef0e70693 100644 --- a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JsonbAccessMode.java +++ b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JsonbAccessMode.java @@ -260,11 +260,11 @@ final boolean record = isRecord(clazz) || Meta.getAnnotation(clazz, JohnzonRecor .orElseGet(() -> naming.translateName(parameter.getName())) : naming.translateName(parameter.getName())); - final JsonbTypeAdapter adapter = getAnnotation(parameter, JsonbTypeAdapter.class); - final JsonbDateFormat dateFormat = getAnnotation(parameter, JsonbDateFormat.class); - final JsonbNumberFormat numberFormat = getAnnotation(parameter, JsonbNumberFormat.class); + final JsonbTypeAdapter adapter = getAnnotationOnParameterOrParameterType(parameter, JsonbTypeAdapter.class); + final JsonbDateFormat dateFormat = getAnnotationOnParameterOrParameterType(parameter, JsonbDateFormat.class); + final JsonbNumberFormat numberFormat = getAnnotationOnParameterOrParameterType(parameter, JsonbNumberFormat.class); final JohnzonConverter johnzonConverter = getAnnotation(parameter, JohnzonConverter.class); - final JsonbTypeDeserializer deserializer = getAnnotation(parameter, JsonbTypeDeserializer.class); + final JsonbTypeDeserializer deserializer = getAnnotationOnParameterOrParameterType(parameter, JsonbTypeDeserializer.class); if (adapter == null && dateFormat == null && numberFormat == null && johnzonConverter == null && deserializer == null) { converters[i] = defaultConverters.get(parameter.getType()); itemConverters[i] = null; @@ -1054,6 +1054,14 @@ private static T getAnnotation(final Parameter param, fin return Meta.findMeta(param.getAnnotations(), api); } + private static T getAnnotationOnParameterOrParameterType(final Parameter param, final Class api) { + final T annotation = getAnnotation(param, api); + if (annotation != null) { + return annotation; + } + return Meta.getAnnotation(param.getType(), api); + } + @Override public void clean(final Class value) { if (Cleanable.class.isInstance(visibility)) { diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/JsonbAdapterTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/JsonbAdapterTest.java index e037ac4e1..34a4e230c 100644 --- a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/JsonbAdapterTest.java +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/JsonbAdapterTest.java @@ -23,6 +23,9 @@ import org.junit.Test; import jakarta.json.bind.adapter.JsonbAdapter; +import jakarta.json.bind.annotation.JsonbCreator; +import jakarta.json.bind.annotation.JsonbProperty; +import jakarta.json.bind.annotation.JsonbTypeAdapter; import static org.junit.Assert.assertEquals; @@ -43,6 +46,18 @@ public void testInheritedAdapterRecognized() { assertEquals("\"#336699\"", json); } + @Test + public void adapterAtConstructorParameterRecognized() { + // given + String json = "{\"innerValue\": \"value\"}"; + + // when + OuterObject object = jsonbRule.fromJson(json, OuterObject.class); + + // then + assertEquals("value", object.innerObject.value); + } + interface ValueType { T value(); } @@ -76,4 +91,37 @@ public ColorId adaptFromJson(String s) throws Exception { return new ColorId(s); } } + + public static class OuterObject { + + private InnerObject innerObject; + + @JsonbCreator + public OuterObject(@JsonbProperty("innerValue") InnerObject object) { + innerObject = object; + } + } + + @JsonbTypeAdapter(InnerObjectAdapter.class) + static class InnerObject { + + private String value; + + public InnerObject(String value) { + this.value = value; + } + } + + public static class InnerObjectAdapter implements JsonbAdapter { + + @Override + public String adaptToJson(InnerObject object) throws Exception { + return object.value; + } + + @Override + public InnerObject adaptFromJson(String value) throws Exception { + return new InnerObject(value); + } + } }