This is an automated email from the ASF dual-hosted git repository.
jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git
The following commit(s) were added to refs/heads/master by this push:
new 3714eba632 @BeanIgnore can be applied to private fields
3714eba632 is described below
commit 3714eba632ec4ce2358679f8d6ec6408e9a13e6b
Author: James Bognar <[email protected]>
AuthorDate: Fri Mar 20 09:31:40 2026 -0400
@BeanIgnore can be applied to private fields
---
.../main/java/org/apache/juneau/BeanContext.java | 1 -
.../src/main/java/org/apache/juneau/BeanMap.java | 1 -
.../src/main/java/org/apache/juneau/BeanMeta.java | 89 ++++++++++++++++++----
.../apache/juneau/BeanProxyInvocationHandler.java | 1 -
.../juneau/InvalidDataConversionException.java | 1 -
.../org/apache/juneau/annotation/BeanIgnore.java | 21 ++++-
.../juneau/annotation/BeanIgnoreAnnotation.java | 25 +++++-
.../juneau/json5/Json5SerializerSession.java | 7 ++
.../org/apache/juneau/objecttools/ObjectRest.java | 1 -
.../juneau/serializer/SerializeException.java | 1 -
.../apache/juneau/serializer/WriterSerializer.java | 1 -
.../rest/swagger/BasicSwaggerProviderSession.java | 8 +-
.../test/java/org/apache/juneau/BeanMap_Test.java | 44 +++++++++++
.../java/org/apache/juneau/ObjectSwap_Test.java | 1 -
.../org/apache/juneau/ParserGenerics_Test.java | 1 -
.../test/java/org/apache/juneau/ProxyBeanTest.java | 1 -
.../annotation/BeanIgnoreAnnotation_Test.java | 2 +-
.../apache/juneau/annotation/BeanIgnore_Test.java | 41 ++++++++++
.../apache/juneau/transforms/ObjectSwapTest.java | 1 -
todo/TODO.md | 5 --
20 files changed, 212 insertions(+), 41 deletions(-)
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanContext.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanContext.java
index 72a548af74..a867940270 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanContext.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanContext.java
@@ -37,7 +37,6 @@ import org.apache.juneau.commons.function.*;
import org.apache.juneau.commons.reflect.*;
import org.apache.juneau.commons.reflect.Visibility;
import org.apache.juneau.cp.*;
-import org.apache.juneau.json.*;
import org.apache.juneau.json5.*;
import org.apache.juneau.marshaller.*;
import org.apache.juneau.serializer.*;
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMap.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMap.java
index 04fbaf5e02..a8088f83d8 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMap.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMap.java
@@ -30,7 +30,6 @@ import org.apache.juneau.annotation.*;
import org.apache.juneau.collections.*;
import org.apache.juneau.commons.reflect.*;
import org.apache.juneau.internal.*;
-import org.apache.juneau.json.*;
import org.apache.juneau.json5.*;
import org.apache.juneau.parser.*;
import org.apache.juneau.swap.*;
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMeta.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMeta.java
index 8329b41717..72fcd484f2 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMeta.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMeta.java
@@ -442,15 +442,8 @@ public class BeanMeta<T> {
bi =
Introspector.getBeanInfo(c2.inner(), stopClass.inner());
else
bi =
Introspector.getBeanInfo(c2.inner(), null);
- if (nn(bi)) {
- for (var pd :
bi.getPropertyDescriptors()) {
- var builder =
normalProps.computeIfAbsent(pd.getName(), n -> BeanPropertyMeta.builder(this,
n));
- if (pd.getReadMethod() != null)
-
builder.setGetter(info(pd.getReadMethod()));
- if (pd.getWriteMethod() != null)
-
builder.setSetter(info(pd.getWriteMethod()));
- }
- }
+ if (nn(bi))
+
mergeJavaBeanPropertyDescriptorsIntoNormalProps(bi, normalProps, propertyNamer);
} else /* Use 'better' introspection */ {
@@ -477,12 +470,11 @@ public class BeanMeta<T> {
if (x.methodType == GETTER) {
// Two getters. Pick the best.
- if (nn(bpm.getter)) {
- if (!
ap.has(Beanp.class, mi) && ap.has(Beanp.class, bpm.getter)) {
- m =
bpm.getter.inner(); // @Beanp annotated method takes precedence.
- } else if
(m.getName().startsWith("is") && bpm.getter.getNameSimple().startsWith("get")) {
- m =
bpm.getter.inner(); // getX() overrides isX().
- }
+ if (nn(bpm.getter)
+ && ((!
ap.has(Beanp.class, mi) && ap.has(Beanp.class, bpm.getter))
+ ||
(m.getName().startsWith("is") &&
bpm.getter.getNameSimple().startsWith("get")))) {
+ // @Beanp on existing
getter takes precedence; else getX() overrides isX().
+ m = bpm.getter.inner();
}
bpm.setGetter(info(m));
}
@@ -1123,6 +1115,7 @@ public class BeanMeta<T> {
var ci = classMeta;
var v = beanContext.getBeanMethodVisibility();
var pn = opt(beanFilter).map(x ->
x.getPropertyNamer()).orElse(beanContext.getPropertyNamer());
+ var suppressedFromBeanIgnoredFields =
findSuppressedPropertyNamesFromIgnoredFields(pn);
classHierarchy.get().stream().forEach(c2 -> {
for (var m : c2.getDeclaredMethods()) {
@@ -1214,7 +1207,7 @@ public class BeanMeta<T> {
if (methodType != UNKNOWN) {
if (nn(bpName) && ! bpName.isEmpty())
n = bpName;
- if (nn(n))
+ if (nn(n) && !
suppressedFromBeanIgnoredFields.contains(n))
l.add(new BeanMethod(n,
methodType, m.inner()));
}
}
@@ -1387,6 +1380,70 @@ public class BeanMeta<T> {
.orElse(null);
}
+ /*
+ * Merges standard JavaBeans {@link BeanInfo} property descriptors into
{@code normalProps}, skipping the class
+ * pseudo-property and logical names suppressed when {@link
BeanIgnore#ignoreAccessors()} is <jk>true</jk> on a field.
+ */
+ @SuppressWarnings({
+ "java:S135" // Two continues: skip class pseudo-property and
names suppressed via @BeanIgnore(ignoreAccessors)
+ })
+ private void mergeJavaBeanPropertyDescriptorsIntoNormalProps(BeanInfo
bi, Map<String,BeanPropertyMeta.Builder> normalProps,
+ PropertyNamer propertyNamer) {
+ var suppressedFromBeanIgnoredFields =
findSuppressedPropertyNamesFromIgnoredFields(propertyNamer);
+ for (var pd : bi.getPropertyDescriptors()) {
+ if (PROP_class.equals(pd.getName()))
+ continue;
+ if
(suppressedFromBeanIgnoredFields.contains(pd.getName()))
+ continue;
+ var builder = normalProps.computeIfAbsent(pd.getName(),
n -> BeanPropertyMeta.builder(this, n));
+ if (pd.getReadMethod() != null)
+ builder.setGetter(info(pd.getReadMethod()));
+ if (pd.getWriteMethod() != null)
+ builder.setSetter(info(pd.getWriteMethod()));
+ }
+ }
+
+ /*
+ * Property names suppressed from getter/setter discovery because a
non-static field with that logical name is
+ * annotated with {@link BeanIgnore @BeanIgnore} and {@link
BeanIgnore#ignoreAccessors()} is <jk>true</jk>.
+ *
+ * <p>
+ * When {@link BeanIgnore#ignoreAccessors()} is <jk>false</jk> (the
default), ignored fields do not suppress
+ * JavaBean accessors so patterns such as {@code @BeanIgnore} on a
private field with a public {@code getX()} still
+ * expose {@code x} when field visibility excludes the field.
+ */
+ @SuppressWarnings({
+ "java:S135" // Two continues in inner loop: skip fields without
@BeanIgnore or without ignoreAccessors
+ })
+ private Set<String>
findSuppressedPropertyNamesFromIgnoredFields(PropertyNamer propertyNamer) {
+ var s = new HashSet<String>();
+ var ap = beanContext.getAnnotationProvider();
+ for (var c2 : classHierarchy.get()) {
+ for (var x : c2.getDeclaredFields()) {
+ if (! x.isNotStatic() || !
ap.has(BeanIgnore.class, x))
+ continue;
+ if (! fieldBeanIgnoreIgnoresAccessors(x))
+ continue;
+ var name = ap.find(x).stream()
+ .filter(x2 -> x2.isType(Beanp.class) ||
x2.isType(Name.class))
+ .map(BeanMeta::name)
+ .filter(Objects::nonNull)
+ .findFirst()
+
.orElse(propertyNamer.getPropertyName(x.getName()));
+ if (nn(name))
+ s.add(name);
+ }
+ }
+ return s;
+ }
+
+ private static boolean fieldBeanIgnoreIgnoresAccessors(FieldInfo x) {
+ for (var bi : x.inner().getAnnotationsByType(BeanIgnore.class))
+ if (bi.ignoreAccessors())
+ return true;
+ return false;
+ }
+
/*
* Finds a bean field by name in the class hierarchy.
*
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanProxyInvocationHandler.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanProxyInvocationHandler.java
index 1944642dd6..4ec58a6baa 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanProxyInvocationHandler.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanProxyInvocationHandler.java
@@ -23,7 +23,6 @@ import static org.apache.juneau.commons.utils.Utils.*;
import java.lang.reflect.*;
import java.util.*;
-import org.apache.juneau.json.*;
import org.apache.juneau.json5.*;
/**
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/InvalidDataConversionException.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/InvalidDataConversionException.java
index 2d6847b10d..2723293874 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/InvalidDataConversionException.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/InvalidDataConversionException.java
@@ -21,7 +21,6 @@ import static org.apache.juneau.commons.utils.Utils.*;
import java.text.*;
-import org.apache.juneau.json.*;
import org.apache.juneau.json5.*;
/**
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/annotation/BeanIgnore.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/annotation/BeanIgnore.java
index f3890b070c..834d96cded 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/annotation/BeanIgnore.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/annotation/BeanIgnore.java
@@ -21,6 +21,9 @@ import static java.lang.annotation.RetentionPolicy.*;
import java.lang.annotation.*;
+import org.apache.juneau.*;
+import org.apache.juneau.commons.reflect.Visibility;
+
/**
* Ignore classes, fields, and methods from being interpreted as bean or bean
components.
*
@@ -30,6 +33,9 @@ import java.lang.annotation.*;
* <li>Classes - Forces bean-like classes to be treated as non-beans.
* <li>Methods - Forces getters/setters to be ignored.
* <li>Fields - Forces bean fields to be ignored.
+ * <li>
+ * Fields — Use {@link #ignoreAccessors()} to also exclude
matching JavaBean accessors from bean metadata (see
+ * {@link #ignoreAccessors()}).
* <li><ja>@Rest</ja>-annotated classes and <ja>@RestOp</ja>-annotated
methods when an {@link #on()} value is specified.
* </ul>
*
@@ -61,11 +67,24 @@ public @interface BeanIgnore {
*/
String[] description() default {};
+ /**
+ * When <jk>true</jk> and this annotation is on a <jk>field</jk>,
JavaBean accessors (<c>getX</c>/<c>setX</c>,
+ * <c>isX</c>) for the same logical property are also excluded from
bean metadata.
+ *
+ * <p>
+ * Default is <jk>false</jk>: {@code @BeanIgnore} on a field only
excludes the field from field-based discovery;
+ * accessors can still expose the property (for example when {@link
BeanContext.Builder#beanFieldVisibility(Visibility) beanFieldVisibility} is
{@link Visibility#NONE NONE}). Set to <jk>true</jk> to
+ * omit the property from serialization and parsing while keeping
accessors for other frameworks.
+ *
+ * @return The annotation value.
+ */
+ boolean ignoreAccessors() default false;
+
/**
* Dynamically apply this annotation to the specified
classes/methods/fields/constructors.
*
* <p>
- * Used in conjunction with {@link
org.apache.juneau.BeanContext.Builder#applyAnnotations(Class...)} to
dynamically apply an annotation to an existing class/method/field/constructor.
+ * Used in conjunction with {@link
BeanContext.Builder#applyAnnotations(Class...)} to dynamically apply an
annotation to an existing class/method/field/constructor.
* It is ignored when the annotation is applied directly to
classes/methods/fields/constructors.
*
* <h5 class='section'>Valid patterns:</h5>
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/annotation/BeanIgnoreAnnotation.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/annotation/BeanIgnoreAnnotation.java
index 2891f670ae..c74062f0fe 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/annotation/BeanIgnoreAnnotation.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/annotation/BeanIgnoreAnnotation.java
@@ -40,7 +40,7 @@ public class BeanIgnoreAnnotation {
private BeanIgnoreAnnotation() {}
/**
- * Applies targeted {@link BeanIgnore} annotations to a {@link
org.apache.juneau.BeanContext.Builder}.
+ * Applies targeted {@link BeanIgnore} annotations to a {@link
BeanContext.Builder}.
*/
public static class Applier extends
AnnotationApplier<BeanIgnore,BeanContext.Builder> {
@@ -83,7 +83,7 @@ public class BeanIgnoreAnnotation {
* Builder class.
*
* <h5 class='section'>See Also:</h5><ul>
- * <li class='jm'>{@link
org.apache.juneau.BeanContext.Builder#annotations(Annotation...)}
+ * <li class='jm'>{@link
BeanContext.Builder#annotations(Annotation...)}
* </ul>
*/
@SuppressWarnings({
@@ -92,6 +92,7 @@ public class BeanIgnoreAnnotation {
public static class Builder extends AppliedAnnotationObject.BuilderTMFC
{
private String[] description = {};
+ private boolean ignoreAccessors;
/**
* Constructor.
@@ -120,6 +121,17 @@ public class BeanIgnoreAnnotation {
return this;
}
+ /**
+ * Sets {@link BeanIgnore#ignoreAccessors()}.
+ *
+ * @param value The new value.
+ * @return This object.
+ */
+ public Builder ignoreAccessors(boolean value) {
+ ignoreAccessors = value;
+ return this;
+ }
+
@Override /* Overridden from AppliedAnnotationObject.Builder */
public Builder on(String...value) {
super.on(value);
@@ -191,16 +203,23 @@ public class BeanIgnoreAnnotation {
private static class Object extends AppliedOnClassAnnotationObject
implements BeanIgnore {
private final String[] description;
+ private final boolean ignoreAccessors;
Object(BeanIgnoreAnnotation.Builder b) {
super(b);
this.description = copyOf(b.description);
+ this.ignoreAccessors = b.ignoreAccessors;
}
@Override /* Overridden from BeanIgnore */
public String[] description() {
return description;
}
+
+ @Override /* Overridden from BeanIgnore */
+ public boolean ignoreAccessors() {
+ return ignoreAccessors;
+ }
}
/** Default value */
@@ -214,7 +233,7 @@ public class BeanIgnoreAnnotation {
* @return A copy of the specified annotation.
*/
public static BeanIgnore copy(BeanIgnore a, VarResolverSession r) {
- return
create().on(r.resolve(a.on())).onClass(a.onClass()).build();
+ return
create().on(r.resolve(a.on())).onClass(a.onClass()).ignoreAccessors(a.ignoreAccessors()).build();
}
/**
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json5/Json5SerializerSession.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json5/Json5SerializerSession.java
index 3a2773d9bc..7d4cb9dbca 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json5/Json5SerializerSession.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json5/Json5SerializerSession.java
@@ -34,11 +34,18 @@ import org.apache.juneau.serializer.*;
* <li class='link'><a class="doclink"
href="https://juneau.apache.org/docs/topics/JsonBasics">JSON Basics</a>
* </ul>
*/
+@SuppressWarnings({
+ "resource", // Pipe writer is wrapped by JsonWriter; caller/pipe owns
lifecycle (same as JsonSerializerSession)
+ "java:S110" // Inheritance depth acceptable for Json5 serializer
session hierarchy (matches JsonSerializerSession)
+})
public class Json5SerializerSession extends JsonSerializerSession {
/**
* Builder class.
*/
+ @SuppressWarnings({
+ "java:S110" // Inheritance depth acceptable for Builder extends
JsonSerializerSession.Builder hierarchy
+ })
public static class Builder extends JsonSerializerSession.Builder {
/**
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/objecttools/ObjectRest.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/objecttools/ObjectRest.java
index 091eca992f..6eb021f98d 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/objecttools/ObjectRest.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/objecttools/ObjectRest.java
@@ -26,7 +26,6 @@ import java.util.*;
import org.apache.juneau.*;
import org.apache.juneau.collections.*;
import org.apache.juneau.commons.reflect.*;
-import org.apache.juneau.json.*;
import org.apache.juneau.json5.*;
import org.apache.juneau.parser.*;
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/serializer/SerializeException.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/serializer/SerializeException.java
index 4cc74cf898..42c56ddae5 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/serializer/SerializeException.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/serializer/SerializeException.java
@@ -23,7 +23,6 @@ import java.text.*;
import java.util.*;
import org.apache.juneau.*;
-import org.apache.juneau.json.*;
import org.apache.juneau.json5.*;
/**
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/serializer/WriterSerializer.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/serializer/WriterSerializer.java
index de5f71418a..4e4b014067 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/serializer/WriterSerializer.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/serializer/WriterSerializer.java
@@ -30,7 +30,6 @@ import org.apache.juneau.commons.collections.*;
import org.apache.juneau.commons.function.*;
import org.apache.juneau.commons.logging.Logger;
import org.apache.juneau.commons.reflect.*;
-import org.apache.juneau.json.*;
/**
* Subclass of {@link Serializer} for character-based serializers.
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/swagger/BasicSwaggerProviderSession.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/swagger/BasicSwaggerProviderSession.java
index 5a93cbef03..cb3da86b20 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/swagger/BasicSwaggerProviderSession.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/swagger/BasicSwaggerProviderSession.java
@@ -294,7 +294,7 @@ public class BasicSwaggerProviderSession {
.appendIf(nec, SWAGGER_tags,
merge(
omSwagger.getList(SWAGGER_tags),
- toList(r.tags(),
"@Swagger(tags) on class {0}", c)
+ toList(r.tags())
)
);
}
@@ -1197,12 +1197,12 @@ public class BasicSwaggerProviderSession {
return JsonMap.ofJson(s);
}
- private JsonList toList(Tag[] aa, String location,
Object...locationArgs) {
+ private JsonList toList(Tag[] aa) {
if (aa.length == 0)
return null;
var ol = new JsonList();
for (var a : aa)
- ol.add(toMap(a, location, locationArgs));
+ ol.add(toMap(a));
return nullIfEmpty(ol);
}
@@ -1243,7 +1243,7 @@ public class BasicSwaggerProviderSession {
return nullIfEmpty(om);
}
- private JsonMap toMap(Tag a, String location, Object...locationArgs) {
+ private JsonMap toMap(Tag a) {
var om = JsonMap.create();
Predicate<String> ne = Utils::ne;
Predicate<Map<?,?>> nem = Utils::ne;
diff --git a/juneau-utest/src/test/java/org/apache/juneau/BeanMap_Test.java
b/juneau-utest/src/test/java/org/apache/juneau/BeanMap_Test.java
index 545fd98766..51c57f850a 100755
--- a/juneau-utest/src/test/java/org/apache/juneau/BeanMap_Test.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/BeanMap_Test.java
@@ -1792,4 +1792,48 @@ class BeanMap_Test extends TestBase {
public List<String> getA() { return Collections.emptyList(); }
public void setA(List<String> v) { a = v; }
}
+
+
//====================================================================================================
+ // containsKey with plain beans vs @Beanp(name="*") dyna/extras map
+
//====================================================================================================
+
+ @Test void z01_containsKey_plainBean_unknownPropertyIsAbsent() {
+ var m = BeanContext.DEFAULT.toBeanMap(new A());
+ assertTrue(m.containsKey("i1"));
+ assertFalse(m.containsKey("noSuchProperty_xyz"));
+ }
+
+ /** Bean with a Map-backed dyna property for extra entries (see
BasicBeans_Test.B). */
+ public static class DynaExtrasBean {
+ @Beanp(name = "*")
+ public Map<String, Object> extras = new LinkedHashMap<>();
+ }
+
+ @Test void z02_containsKey_dynaBean_unknownKeyNotInExtrasMap() {
+ var bean = new DynaExtrasBean();
+ var m = BeanContext.DEFAULT.toBeanMap(bean);
+ // Dyna map is empty; @Beanp(name="*") does not make arbitrary
keys appear as present.
+ assertFalse(m.containsKey("notDefinedAnywhere"));
+ assertEquals(set(), m.keySet());
+ }
+
+ @Test void z03_containsKey_dynaBean_trueAfterPutIntoExtras() {
+ var bean = new DynaExtrasBean();
+ var m = BeanContext.DEFAULT.toBeanMap(bean);
+ m.put("dynamicOnly", 123);
+ assertTrue(m.containsKey("dynamicOnly"));
+ assertEquals(123, bean.extras.get("dynamicOnly"));
+ }
+
+ /**
+ * {@link BeanMap#getPropertyMeta(String)} falls back to the dyna ("*")
property meta for unknown names so that
+ * {@link BeanMap#put(String, Object)} can route writes into the extras
map; that must not imply the key exists for
+ * {@link Map#containsKey(Object)}.
+ */
+ @Test void
z04_getPropertyMetaMayFallbackToDyna_containsKeyStillFalseForUnknownKey() {
+ var bean = new DynaExtrasBean();
+ var m = BeanContext.DEFAULT.toBeanMap(bean);
+ assertNotNull(m.getPropertyMeta("phantomKey"));
+ assertFalse(m.containsKey("phantomKey"));
+ }
}
\ No newline at end of file
diff --git a/juneau-utest/src/test/java/org/apache/juneau/ObjectSwap_Test.java
b/juneau-utest/src/test/java/org/apache/juneau/ObjectSwap_Test.java
index a2008764a2..186efdb25c 100755
--- a/juneau-utest/src/test/java/org/apache/juneau/ObjectSwap_Test.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/ObjectSwap_Test.java
@@ -19,7 +19,6 @@ package org.apache.juneau;
import static org.junit.jupiter.api.Assertions.*;
import org.apache.juneau.collections.*;
-import org.apache.juneau.json.*;
import org.apache.juneau.json5.*;
import org.apache.juneau.parser.*;
import org.apache.juneau.serializer.*;
diff --git
a/juneau-utest/src/test/java/org/apache/juneau/ParserGenerics_Test.java
b/juneau-utest/src/test/java/org/apache/juneau/ParserGenerics_Test.java
index 9e6782d601..c899c4abe2 100755
--- a/juneau-utest/src/test/java/org/apache/juneau/ParserGenerics_Test.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/ParserGenerics_Test.java
@@ -20,7 +20,6 @@ import static org.junit.jupiter.api.Assertions.*;
import java.util.*;
-import org.apache.juneau.json.*;
import org.apache.juneau.json5.*;
import org.junit.jupiter.api.*;
diff --git a/juneau-utest/src/test/java/org/apache/juneau/ProxyBeanTest.java
b/juneau-utest/src/test/java/org/apache/juneau/ProxyBeanTest.java
index 392cc2b36f..b8287bf682 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/ProxyBeanTest.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/ProxyBeanTest.java
@@ -19,7 +19,6 @@ package org.apache.juneau;
import static org.junit.jupiter.api.Assertions.*;
import org.apache.juneau.html.*;
-import org.apache.juneau.json.*;
import org.apache.juneau.json5.*;
import org.apache.juneau.msgpack.*;
import org.apache.juneau.oapi.*;
diff --git
a/juneau-utest/src/test/java/org/apache/juneau/annotation/BeanIgnoreAnnotation_Test.java
b/juneau-utest/src/test/java/org/apache/juneau/annotation/BeanIgnoreAnnotation_Test.java
index 63bb6fe1b0..38897e5fe7 100644
---
a/juneau-utest/src/test/java/org/apache/juneau/annotation/BeanIgnoreAnnotation_Test.java
+++
b/juneau-utest/src/test/java/org/apache/juneau/annotation/BeanIgnoreAnnotation_Test.java
@@ -49,7 +49,7 @@ class BeanIgnoreAnnotation_Test extends TestBase {
.build();
@Test void a01_basic() {
- assertBean(a1, "description,on,onClass", "[a],[b],[X1]");
+ assertBean(a1, "description,ignoreAccessors,on,onClass",
"[a],false,[b],[X1]");
}
@Test void a02_testEquivalency() {
diff --git
a/juneau-utest/src/test/java/org/apache/juneau/annotation/BeanIgnore_Test.java
b/juneau-utest/src/test/java/org/apache/juneau/annotation/BeanIgnore_Test.java
index 7fc6d151e7..e2260d8aa1 100644
---
a/juneau-utest/src/test/java/org/apache/juneau/annotation/BeanIgnore_Test.java
+++
b/juneau-utest/src/test/java/org/apache/juneau/annotation/BeanIgnore_Test.java
@@ -17,6 +17,7 @@
package org.apache.juneau.annotation;
import static org.apache.juneau.TestUtils.*;
+import static org.junit.jupiter.api.Assertions.*;
import org.apache.juneau.*;
import org.apache.juneau.json5.*;
@@ -120,4 +121,44 @@ class BeanIgnore_Test extends TestBase {
@Test void a04_beanIgnoreOnBean_usingConfig() {
assertSerialized(new Bc(),
Json5Serializer.DEFAULT.copy().applyAnnotations(B1cConfig.class).build(),
"{f2:2,f3:'xxx',f4:'xxx'}");
}
+
+
//------------------------------------------------------------------------------------------------------------------
+ // @BeanIgnore on private field: suppress accessor pair from bean
metadata (default + JavaBean introspector)
+
//------------------------------------------------------------------------------------------------------------------
+
+ public static class PrivateFieldIgnoredWithAccessors {
+ public String visible = "ok";
+
+ @BeanIgnore(ignoreAccessors = true)
+ private String foo = "secret";
+
+ public String getVisible() {
+ return visible;
+ }
+
+ public void setVisible(String value) {
+ visible = value;
+ }
+
+ public String getFoo() {
+ return foo;
+ }
+
+ public void setFoo(String value) {
+ foo = value;
+ }
+ }
+
+ @Test void a05_beanIgnoreOnPrivateFieldSuppressesGetterProperty() {
+ var bm =
BeanContext.DEFAULT.getBeanMeta(PrivateFieldIgnoredWithAccessors.class);
+ assertFalse(bm.getProperties().containsKey("foo"), () ->
"properties: " + bm.getProperties().keySet());
+ assertJson("{visible:'ok'}", new
PrivateFieldIgnoredWithAccessors());
+ }
+
+ @Test void
a06_beanIgnoreOnPrivateField_suppressedWithJavaBeanIntrospector() {
+ var bc = BeanContext.create().useJavaBeanIntrospector().build();
+ var s = Json5Serializer.DEFAULT.copy().beanContext(bc).build();
+
assertFalse(bc.getBeanMeta(PrivateFieldIgnoredWithAccessors.class).getProperties().containsKey("foo"));
+ assertEquals("{visible:'ok'}", s.serialize(new
PrivateFieldIgnoredWithAccessors()));
+ }
}
\ No newline at end of file
diff --git
a/juneau-utest/src/test/java/org/apache/juneau/transforms/ObjectSwapTest.java
b/juneau-utest/src/test/java/org/apache/juneau/transforms/ObjectSwapTest.java
index 902598b0b8..8829f6bbd8 100644
---
a/juneau-utest/src/test/java/org/apache/juneau/transforms/ObjectSwapTest.java
+++
b/juneau-utest/src/test/java/org/apache/juneau/transforms/ObjectSwapTest.java
@@ -22,7 +22,6 @@ import org.apache.juneau.*;
import org.apache.juneau.MediaType;
import org.apache.juneau.html.*;
import org.apache.juneau.json.*;
-import org.apache.juneau.json5.*;
import org.apache.juneau.serializer.*;
import org.apache.juneau.serializer.WriterSerializer.Builder;
import org.apache.juneau.swap.*;
diff --git a/todo/TODO.md b/todo/TODO.md
index 94557524b9..9bca0bcda1 100644
--- a/todo/TODO.md
+++ b/todo/TODO.md
@@ -7,9 +7,6 @@
- Need an easier way to specify this header:
Content-Disposition: attachment; filename="example.pdf"
-- Figure out why this needs a cast:
- private static final Json5 JSON5_LENIENT = new
Json5(Json5Serializer.DEFAULT,
(Json5Parser)Json5Parser.create().ignoreUnknownBeanProperties().build());
-
- A comprehensive plan for handling large data sets using Suppliers and
Consumers?
- CSV format supports property names in headers (make sure it can work with
Suppliers and Consumers above)
@@ -18,9 +15,7 @@
- RestResponse needs a setSerializer() command.
-- Verify that you can add @BeanIgnore on a private field with getters/setters.
- On RestClient when logging with FULL, calling
RestREsponse.getContent().asString() causes a stream closed exception.
- Possibility of adding convenience classes for
okhttp3.mockwebserver.Dispatcher?
-- BeanMap.containsKey not working correctly on non-existent properties?
- Duration.ofDays(7) serialized in hours?
\ No newline at end of file