I've found myself writing a method like this:
boolean isEmpty(MyStruct myStruct) {
return (myStruct.getStringA() == null || myStruct.getStringA().isEmpty())
&& (myStruct.getListB() == null || myStruct.getListB().isEmpty());
}
And then imagine this struct with lots of other properties and other nested lists, and you can imagine that this method gets very big and is tightly coupled to the data model.
Does Apache Commons, or Spring, or some other FOSS utility have the ability to recursively reflectively walk an object graph and determine that it's basically devoid of any useful data, other than the holders for Lists, Arrays, Maps, and such? So that I can开发者_运维知识库 just write:
boolean isEmpty(MyStruct myStruct) {
return MagicUtility.isObjectEmpty(myStruct);
}
Thanks to Vladimir for pointing me in the right direction (I gave you an upvote!) although my solution uses PropertyUtils
rather than BeanUtils
I did have to implement it but it wasn't hard. Here's the solution. I only did it for Strings and Lists since that's all I happen to have at the moment. Could be extended for Maps and Arrays.
import java.lang.reflect.InvocationTargetException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang.StringUtils;
public class ObjectUtils {
/**
* Tests an object for logical emptiness. An object is considered logically empty if its public gettable property
* values are all either null, empty Strings or Strings with just whitespace, or lists that are either empty or
* contain only other logically empty objects. Currently does not handle Maps or Arrays, just Lists.
*
* @param object
* the Object to test
* @return whether object is logically empty
*
* @author Kevin Pauli
*/
@SuppressWarnings("unchecked")
public static boolean isObjectEmpty(Object object) {
// null
if (object == null) {
return true;
}
// String
else if (object instanceof String) {
return StringUtils.isEmpty(StringUtils.trim((String) object));
}
// List
else if (object instanceof List) {
boolean allEntriesStillEmpty = true;
final Iterator<Object> iter = ((List) object).iterator();
while (allEntriesStillEmpty && iter.hasNext()) {
final Object listEntry = iter.next();
allEntriesStillEmpty = isObjectEmpty(listEntry);
}
return allEntriesStillEmpty;
}
// arbitrary Object
else {
try {
boolean allPropertiesStillEmpty = true;
final Map<String, Object> properties = PropertyUtils.describe(object);
final Iterator<Entry<String, Object>> iter = properties.entrySet().iterator();
while (allPropertiesStillEmpty && iter.hasNext()) {
final Entry<String, Object> entry = iter.next();
final String key = entry.getKey();
final Object value = entry.getValue();
// ignore the getClass() property
if ("class".equals(key))
continue;
allPropertiesStillEmpty = isObjectEmpty(value);
}
return allPropertiesStillEmpty;
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
}
}
you can combine Appache Common StringUtils.isEmpty()
method with BeanUtils.getProperties()
.
Hey Kevin, Haven't seen anything like the method you asked for (interested in it myself as well), however, have you considered using reflection to query your object on runtime?
I don't know a library that does this, but using reflection, it is not too hard to implement by yourself. You can easily loop over getter methods of a class and decide whether the instance has a value set, you can further decide the outcome depending on the getter's return type.
The hard part is to define which members of a class you consider being "useful data". Just leaving out Lists, Maps and Arrays probably won't get you far.
I would actually consider writing your own Annotation type in order to "mark" the appropiate "useful" members (which could then be noticed+handled by the reflection code). I don't know if this is an elegant approach to your problem, since I don't know how many classes are affected.
I can't really imagine a situation where something completely devoid of any content in a whole object graph would get in to an application. Furthermore, what exactly would be considered "empty"? Would that just refer to strings and collections? Would a string with only whitespace count? What about numbers... would any number make the object non-empty, or would specific numbers like -1 count as empty? As yet another issue, it doesn't seem like it'd generally be useful to know if there is NO content in an object... typically you need to ensure that specific fields have specific data, etc. There are too many possibilities for some general method like this to make sense, in my opinion.
Maybe a more complete validation system like JSR-303 would work better. The reference implementation for that is Hibernate Validator.
Although none of the above utility methods can be generic. The only way (to my knowledge) is to compare with a empty object. Create a instance of the object (with no properties set) and use ".equals" method to compare. Make sure that equals is properly implemented to true for 2 equal non empty objects & true for 2 empty objects.
精彩评论