feat: field visibilty buggy for booleans and not compliant with specification
Signed-off-by: Jean-Louis Monteiro <jlmonteiro@tomitribe.com>
diff --git a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/DefaultPropertyVisibilityStrategy.java b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/DefaultPropertyVisibilityStrategy.java
index 919a895..67ea85c 100644
--- a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/DefaultPropertyVisibilityStrategy.java
+++ b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/DefaultPropertyVisibilityStrategy.java
@@ -50,6 +50,11 @@
return strategy == this ? isFieldVisible(field, root, useGetter) : strategy.isVisible(field);
}
+ /**
+ * If the field is not public then it's of course not visible. If the field is public then we need to look at the
+ * accessors. If there is a private/protected/default accessor for it then it overrides and the field is not visible
+ * But if there is no accessor for it, then it's visible.
+ */
private boolean isFieldVisible(final Field field, final Class<?> root, final boolean useGetter) {
if (!Modifier.isPublic(field.getModifiers())) {
return false;
@@ -57,16 +62,16 @@
// 3.7.1. Scope and Field access strategy
// note: we should bind the class since a field of a parent class can have a getter in a child
if (!useGetter) {
- return !hasMethod(root, BeanUtil.setterName(field.getName()));
+ return !hasMethod(root, BeanUtil.setterName(field.getName()), field.getType());
}
- final String capitalizedName = BeanUtil.capitalize(field.getName());
- return !hasMethod(root, "get" + capitalizedName) || hasMethod(root, "is" + capitalizedName);
+ return !hasMethod(root, BeanUtil.getterName(field.getName(), field.getType()));
}
private boolean hasMethod(Class<?> clazz, String methodName, Class<?>... paramTypes) {
try {
- clazz.getDeclaredMethod(methodName, paramTypes);
- return true;
+ final Method declaredMethod = clazz.getDeclaredMethod(methodName, paramTypes);
+ return !Modifier.isPublic(declaredMethod.getModifiers());
+
} catch (NoSuchMethodException e) {
final Class<?> superclass = clazz.getSuperclass();
if (superclass == Object.class) {
diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/DefaultPropertyVisibilityStrategyTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/DefaultPropertyVisibilityStrategyTest.java
index 25e9b5f..ac32834 100644
--- a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/DefaultPropertyVisibilityStrategyTest.java
+++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/DefaultPropertyVisibilityStrategyTest.java
@@ -18,6 +18,7 @@
*/
package org.apache.johnzon.jsonb;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -57,6 +58,61 @@
}
}
+ @Test
+ public void matchingPrivateAccessors() throws Exception {
+ try (final Jsonb jsonb = JsonbBuilder.create()) {
+ assertEquals("{}", jsonb.toJson(new PublicFieldWithPrivateAccessors()));
+ assertEquals("{}", jsonb.toJson(new PublicBooleanFieldWithPrivateAccessors()));
+
+ {
+ final PublicFieldWithPrivateAccessors unmarshalledObject =
+ jsonb.fromJson("{\"foo\":\"bad\"}", PublicFieldWithPrivateAccessors.class);
+ assertEquals("bar", unmarshalledObject.foo);
+ }
+ {
+ final PublicBooleanFieldWithPrivateAccessors unmarshalledObject =
+ jsonb.fromJson("{\"foo\":\"false\"}", PublicBooleanFieldWithPrivateAccessors.class);
+ assertEquals(true, unmarshalledObject.foo);
+ }
+ }
+ }
+
+ @Test
+ public void noMatchingPrivateAccessors() throws Exception {
+ try (final Jsonb jsonb = JsonbBuilder.create()) {
+ assertEquals("{\"foo\":\"bar\"}", jsonb.toJson(new PublicFieldWithoutMatchingPrivateAccessors()));
+
+ final PublicFieldWithoutMatchingPrivateAccessors unmarshalledObject =
+ jsonb.fromJson("{\"foo\":\"good\"}", PublicFieldWithoutMatchingPrivateAccessors.class);
+ assertEquals("good", unmarshalledObject.foo);
+ }
+ }
+
+ @Test
+ public void oneAccessorOnly() throws Exception {
+ try (final Jsonb jsonb = JsonbBuilder.create()) {
+ assertEquals("{}", jsonb.toJson(new PublicFieldWithMatchingPrivateGetterAccessors()));
+ assertEquals("{\"foo\":\"bar\"}", jsonb.toJson(new PublicFieldWithMatchingPrivateSetterAccessors()));
+ assertEquals("{\"foo\":\"bar\"}", jsonb.toJson(new PublicFieldWithPublicAccessors()));
+
+ {
+ final PublicFieldWithMatchingPrivateGetterAccessors unmarshalledObject =
+ jsonb.fromJson("{\"foo\":\"good\"}", PublicFieldWithMatchingPrivateGetterAccessors.class);
+ assertEquals("good", unmarshalledObject.foo);
+ }
+ {
+ final PublicFieldWithMatchingPrivateSetterAccessors unmarshalledObject =
+ jsonb.fromJson("{\"foo\":\"bad\"}", PublicFieldWithMatchingPrivateSetterAccessors.class);
+ assertEquals("bar", unmarshalledObject.foo);
+ }
+ {
+ final PublicFieldWithPublicAccessors unmarshalledObject =
+ jsonb.fromJson("{\"foo\":\"good\"}", PublicFieldWithPublicAccessors.class);
+ assertEquals("good", unmarshalledObject.foo);
+ }
+ }
+ }
+
public static class HideAll extends DefaultPropertyVisibilityStrategy {
@Override
public boolean isVisible(final Field field) {
@@ -81,6 +137,64 @@
}
}
+ // if there is a matching private getter/setter, the field even if it's public must be ignored
+ public static final class PublicFieldWithPrivateAccessors {
+ public String foo = "bar";
+
+ private String getFoo() {
+ return foo;
+ }
+
+ private void setFoo(final String foo) {
+ this.foo = foo;
+ }
+ }
+
+ public static final class PublicFieldWithPublicAccessors {
+ public String foo = "bar";
+
+ public String getFoo() {
+ return foo;
+ }
+
+ public void setFoo(final String foo) {
+ this.foo = foo;
+ }
+ }
+
+ public static final class PublicBooleanFieldWithPrivateAccessors {
+ public boolean foo = true;
+
+ private boolean isFoo() {
+ return foo;
+ }
+
+ private void setFoo(final boolean foo) {
+ this.foo = foo;
+ }
+ }
+
+ // if there is not matching getter/setter for a public field, then the field is used directly
+ public static final class PublicFieldWithoutMatchingPrivateAccessors {
+ public String foo = "bar";
+ }
+
+ public static final class PublicFieldWithMatchingPrivateGetterAccessors {
+ public String foo = "bar";
+
+ private String getFoo() {
+ return foo;
+ }
+ }
+
+ public static final class PublicFieldWithMatchingPrivateSetterAccessors {
+ public String foo = "bar";
+
+ private void setFoo(final String foo) {
+ this.foo = foo;
+ }
+ }
+
@JsonbVisibility(MyVisibility.class)
public static final class TheClass {
@JsonbProperty