PropertyUtil { public static Map<String, Prop> getProperties(final Object object) { Objects.requireNonNull(object); LOGGER.trace("getProperties called for {}", object); final Map<String, Prop> propMap = new HashMap<>(); final Class<?> clazz = object.getClass(); getPropsFromFields(object, propMap); getPropsFromMethods(object, propMap); return propMap .entrySet() .stream() .filter(e -> { if (e.getValue().getter == null || e.getValue().setter == null) { LOGGER.trace("Invalid property " + e.getKey() + " on " + clazz.getName()); return false; } return true; }) .collect(Collectors.toMap(Entry::getKey, Entry::getValue)); } private  PropertyUtil(); static void walkObjectTree(final Object object,
                                      final Predicate<Prop> propFilter,
                                      final Consumer<Prop> propConsumer); static Map<String, Prop> getProperties(final Object object);  }
@Test void getProperties() { MyClass myClass = new MyClass(); MyClass myChildClass1 = new MyClass(); MyClass myChildClass2 = new MyClass(); myClass.setMyClass(myChildClass1); final Map<String, PropertyUtil.Prop> propMap = PropertyUtil.getProperties(myClass); assertThat(propMap) .hasSize(4); assertThat(propMap.values().stream() .map(PropertyUtil.Prop::getParentObject) .map(System::identityHashCode) .distinct() .collect(Collectors.toList())) .containsExactly(System.identityHashCode(myClass)); testProp(propMap, "myBoolean", true, false, myClass::isMyBoolean, boolean.class); testProp(propMap, "myInt", 99, 101, myClass::getMyInt, int.class); testProp(propMap, "myString", "abc", "def", myClass::getMyString, String.class); testProp(propMap, "myClass", myChildClass1, myChildClass2, myClass::getMyClass, MyClass.class); }
@Test void getProperties_fieldProps() { AnnosOnFields annosOnFields = new AnnosOnFields(); annosOnFields.setIncludedField("yes"); annosOnFields.setReadOnlyField("cheese"); annosOnFields.setIgnoredField("No"); final Map<String, PropertyUtil.Prop> propMap = PropertyUtil.getProperties(annosOnFields); assertThat(propMap) .hasSize(2); assertThat(propMap.values().stream() .map(PropertyUtil.Prop::getParentObject) .map(System::identityHashCode) .distinct() .collect(Collectors.toList())) .containsExactly(System.identityHashCode(annosOnFields)); testProp(propMap, "includedField", "yes", "yes2", annosOnFields::getIncludedField, String.class); testProp(propMap, "readOnlyField", "cheese", "cheese2", annosOnFields::getReadOnlyField, String.class); PropertyUtil.Prop includedFieldProp = propMap.get("includedField"); Assertions.assertThat(includedFieldProp.hasFieldAnnotation(JsonProperty.class)) .isTrue(); Assertions.assertThat(includedFieldProp.hasFieldAnnotation(JsonPropertyDescription.class)) .isTrue(); Assertions.assertThat(includedFieldProp.hasGetterAnnotation(JsonProperty.class)) .isFalse(); Assertions.assertThat(includedFieldProp.hasGetterAnnotation(JsonPropertyDescription.class)) .isFalse(); Assertions.assertThat(includedFieldProp.hasAnnotation(JsonProperty.class)) .isTrue(); Assertions.assertThat(includedFieldProp.hasAnnotation(JsonPropertyDescription.class)) .isTrue(); PropertyUtil.Prop readOnlyFieldProp = propMap.get("readOnlyField"); Assertions.assertThat(includedFieldProp.hasFieldAnnotation(JsonProperty.class)) .isTrue(); Assertions.assertThat(includedFieldProp.hasFieldAnnotation(JsonPropertyDescription.class)) .isTrue(); Assertions.assertThat(readOnlyFieldProp.hasFieldAnnotation(ReadOnly.class)) .isTrue(); }