The idea is to have a POJO like:
class MyBean {
long id;
int count;
public void setCount(int count){
this.count = count;
}
}
Now, I need that the count be stored automatically as:
put("count", count);
or simply put, put("fieldname", fieldvalue);
Is there a library that can be used for this purpose that MyBean can exetnd to? I can easily do a copy constructor or something, however, the point here is automation and besides there are so many models in my app that will be having this Map stored POJO values...
You could create a simple PropertyMapGenerator using Apache Commons BeanUtils' PropertyUtils
public class PropertyMapGenerator {
public static Map<String, Object> getPropertyMap(Object object) {
HashMap<String, Object> propertyMap = new HashMap<>();
// retrieve descriptors for all properties
PropertyDescriptor[] descriptors = PropertyUtils.getPropertyDescriptors(object);
for (PropertyDescriptor descriptor : descriptors) {
// check if there is a reader method for this property i.e. if it can be accessed
if (descriptor.getReadMethod() != null) {
String name = descriptor.getName();
try {
propertyMap.put(name, PropertyUtils.getProperty(object, name));
} catch (Exception e) {
// handle this properly
e.printStackTrace();
}
}
}
return propertyMap;
}
}
Now you can simply pass your POJOs to this generator:
Map<String, Object> propertyMap = PropertyMapGenerator.getPropertyMap(myBean);
Related
I'm using the method below to get the names of all getter methods of a class:
private static Map<String, Object> fetchGetterMethods(Object object) {
Map<String, Object> result = new HashMap<String, Object>();
BeanInfo info;
try {
info = Introspector.getBeanInfo(object.getClass());
for (PropertyDescriptor pd : info.getPropertyDescriptors()) {
Method reader = pd.getReadMethod();
if (reader != null) {
String name = pd.getName();
if (!"class".equals(name)) {
try {
Object value = reader.invoke(object);
result.put(name, value);
} catch (Exception e) {
}
}
}
}
} catch (IntrospectionException e) {
} finally {
return result;
}
}
I would like to skip the getter methods annotated with #Transient. How can I implement this?
#Transient
public boolean isValid() {
}
All you need is reader.getAnnotation(Transient.class) - if that returns something (it'll be an instance of Transient), it had the annotation. If it returns null, it did not. You can only do this to annotations whose definition is explicitly annotated with retentionlevel runtime, but assuming you're talking about JPA's #Transient, it is.
Note that writing a return statement in a finally block is absolutely not something you want to do.
You should use the Method#isAnnotationPresent method.
if (!reader.isAnnotationPresent(Transient.class)) {
// do work
}
This is a convenience method for the solution suggested by #rzwitserloot, and is the equivalent to:
if (reader.getAnnotation(Transient.class) == null) {
Right now my cache looks like the following:
public class TestCache {
private LoadingCache<String, List<ObjectABC>> cache;
TestCache() {
cache = CacheBuilder.newBuilder().expireAfterAccess(10, TimeUnit.MINUTES).maximumSize(25)
.build(new CacheLoader<String, List<ObjectABC>>(
) {
#Override
public List<ObjectABC> load(String key) throws Exception {
// TODO Auto-generated method stub
return addCache(key);
}
});
}
private List<ObjectABC> addCache(String key) {
final JoiObjectMapper mapper = new JoiObjectMapper();
final Collection<File> allConfigFiles = FileUtils.listFiles(new File(key), null, true);
final List<ObjectABC> configsList = new ArrayList<>();
allConfigFiles.forEach(configFile -> {
try {
configsList.add(mapper.readValue(configFile, new TypeReference<ObjectABC>() {
}));
} catch (Exception e) {
throw new RuntimeException(e);
}
});
return configsList;
}
public List<ObjectABC> getEntry(String key) {
try {
return cache.get(key);
} catch (ExecutionException e) {
throw new NonRetriableException(String.format(
"Exception occured while trying to get data from cache for the key : {} Exception: {}",
key.toString(), e));
}
}
}
In the above code, when I pass a String key (which is path to a local folder) it takes all the files present in that location and maps them to ObjectABC using ObjectMapper.
Now my problem is that I want to instead have a generic loading cache like
LoadingCache<String, List<Object>>.
And I want to map files in different folders to different Objects, e.g. map files in /root/Desktop/folder1 to List<ObjectABC> and map files in /root/Desktop/folder2 to List<ObjectDEF> and be able to store and retrieve that information from the cache.
How can I pass to the cache the information of which object to use for mapping?
You can create a custom class wrapping a LoadingCache<Key<?>, Object> like that:
class HeterogeneousCache {
private final LoadingCache<Key<?>, Object> cache;
public <T> T get(Key<T> key) throws ExecutionException {
return key.getType().cast(cache.get(key));
}
}
#Value // provides constructor, getters, equals, hashCode
class Key<T> {
private final String identifier;
private final Class<T> type;
}
(I used Lombok's #Value annotation for simplicity)
Of course, this is just a stub and you might need to adapt this to your needs. The main problem might be that you can't get a Class<List<ObjectABC>> - you can only get a Class<List>. The easiest way out of this is to wrap the List<ObjectABC> in some custom type. The harder way (not recommended) is to use Guava's TypeToken.
Attribution: This answer is based on the post by Frank Appel entitled How to Map Distinct Value Types Using Java Generics, which itself is based on Joshua Bloch's typesafe hetereogeneous containers from Effective Java.
Edit: A Complete Solution
Since the OP wants List<T> as result, and since he needs instances of TypeReference<T>, I replaced Class<T> with TypeReference<T> in Key<T>:
#Value // provides constructor, getters, equals, hashCode
class Key<T> {
private final String identifier;
private final TypeReference<T> typeReference;
}
Here's how CustomHeterogeneousCache looks now:
class CustomHeterogeneousCache {
private final LoadingCache<Key<?>, List<?>> cache = CacheBuilder.newBuilder()
.expireAfterAccess(10, TimeUnit.MINUTES)
.maximumSize(25)
.build(CacheLoader.from(this::computeEntry));
#SuppressWarnings("unchecked")
public <T> List<T> getEntry(Key<T> key) {
return (List<T>) cache.getUnchecked(key);
}
private <T> List<T> computeEntry(Key<T> key) {
final JoiObjectMapper mapper = new JoiObjectMapper();
final Collection<File> allConfigFiles = FileUtils.listFiles(new File(key.getIdentifier()), null, true);
return allConfigFiles.stream()
.map(configFile -> {
try {
return mapper.readValue(configFile, key.getTypeReference());
} catch (Exception e) {
throw new RuntimeException(e);
}
})
.collect(Collectors.toList());
}
}
Since implementations of TypeReference do not have value semantics, the user must make sure that every Key is created once, and then only referenced, e.g.:
class Keys {
public static final Key<ObjectABC> ABC = new Key<>("/root/Desktop/folder1", new TypeReference<ObjectABC>() {
});
public static final Key<ObjectDEF> DEF = new Key<>("/root/Desktop/folder2", new TypeReference<ObjectDEF>() {
});
}
Getting a java.util.Set of objects which has only key-value pairs as input like below
public class KeyValueObject {
String key;
String value;
// good old constructor, setters and getters
}
Input Object:
java.util.Set inputObject;
KeyValuePair pair1 = new KeyValuePair("Name":"John");
KeyValuePair pair2 = new KeyValuePair("Age":"28");
KeyValuePair pair3 = new KeyValuePair("Location":"Cincinnati");
inputObject.add(pair1);
inputObject.add(pair2);
inputObject.add(pair3);
With the "inputObject" as the request coming in, how to convert this to a simple POJO object which has all the keys described above as individual parameters like below:
Public class SimplePojoObject {
private String name;
private String age;
private String location;
// Good old setters and getters
}
The incoming object has around 52 objects and that is why the manual way of mapping is not the right way to solve this issue. Please suggest on any possible way of mapping this data
You can do it like this:
Convert Set<KeyValueObject> to a JsonNode (or Map<String, String>) object.
Convert the generated JsonNode (or Map<String, String>) object to SimplePojoObject using ObjectMapper (you can also use Gson library instead of ObjectMapper)
If you already have a Map<String, String> object instead of Set<KeyValueObject> then you can do it in just one line:
SimplePojoObject simplePojoObject = new ObjectMapper().convertValue(map, SimplePojoObject.class);
The easiest way is to write a small method that calls the setters:
public SimplePojoObject buildSimplePojoObject(Set<KeyValuePair> properties) {
SimplePojoObject result = new SimplePojoObject();
for (KeyValuePair prop : properties) {
switch (prop.getKey()) {
case "Name":
result.setName(prop.getValue());
break;
case "Age":
result.setAge(prop.getValue());
break;
case "Location":
result.setLocation(prop.getValue());
break;
default:
// Throw an exception or ignore it.
throw new IllegalArgumentException("Unknown property "+ prop.getKey());
}
}
return result;
}
But if you want to do that dynamically, you certainly could:
public SimplePojoObject buildSimplePojoObject(Set<KeyValuePair> properties) {
SimplePojoObject result = new SimplePojoObject();
Lookup l = MethodHandles.publicLookup();
MethodType mt = MethodType.methodType(void.class, String.class);
for (KeyValuePair prop : properties) {
MethodHandle mh = l.findVirtual(SimplePojoObject.class, "set" + prop.getKey());
try {
mh.invokeExact(result, prop.getValue());
} catch (Error | RuntimeException e) {
throw e;
} catch (Throwable t) {
// MethodHandle.invokeExact is declared to throw Throwable, so we have to catch it.
throw new RuntimeException(e);
}
}
return result;
}
I have a DTO with some GSon annotation.
My probleme is that the value of these annotations have to change if my application run in developpement or in staging or in production...
For the moment, I have to package my application with the different value and I want this to be automatic... It is in a Spring Boot application and I want to use the spring.profiles.active to tell my application to take the right serializedName
Here is the kind of code I use
// Tests
// #SerializedName("customfield_10123")
// Prod
#SerializedName("customfield_10114")
private ActionDto action;
I hope there is a better way to do it?
Here is a very crude example on how you can achieve what you want:
First create a propery file for each possible profile (name can be anything, but the profile must be on the name):
application-dev.properties
application-prod.properties
...
Populate the properties with the values you want for each key accordingly to each profile:
test=abc.test
...
Annotate your POJOs:
public class Foo {
#SerializedName("${test}")
private String name;
...
}
Create a custom serializer for your class, which will interpret the custom names, something like this:
public class FooSerializer implements JsonSerializer<Foo> {
private static final Pattern PATTERN = Pattern.compile("\\$\\{(.*)\\}");
private static Properties props;
static {
try {
Resource resource = new ClassPathResource(String.format("/application-%s.properties", System.getProperty("spring.profiles.active")));
props = PropertiesLoaderUtils.loadProperties(resource);
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public JsonElement serialize(Foo foo, Type type, JsonSerializationContext jsonSerializationContext) {
Field[] fields = foo.getClass().getDeclaredFields();
JsonObject object = new JsonObject();
for (Field field : fields) {
field.setAccessible(true);
String name = field.getName();
if (field.isAnnotationPresent(SerializedName.class)) {
String value = field.getAnnotation(SerializedName.class).value();
Matcher matcher = PATTERN.matcher(value);
if (matcher.find()) {
name = props.get(matcher.group(1)).toString();
} else {
name = value;
}
}
try {
if (field.get(foo) != null) {
object.addProperty(name, field.get(foo).toString());
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
return object;
}
}
Now you just need to register your custom serializer and you are good to go:
Gson gson = new GsonBuilder().registerTypeAdapter(Foo.class, new FooSerializer()).setPrettyPrinting().create();
Of course there may be better ways to recover the properties file according to the active profile, but the given snippet should be enough to get you going. Also, you need to consider the fact that there may be multiple profiles active at any given time, so if that is your scenario, you need to take it into consideration before recovering the properties.
You don't even need the regex part if you will always want to use the value from the properties. I used a regex to allow both cases.
If something wasn't clear, please let me know and I will try to improve it.
EDIT:
For the deserialization I can't come up with anything very good, so here is an example which I think is far from OK, but gets the job done:
Functional interface:
public interface Converter {
Object convert(String s);
}
Deserializer:
public class FooDeserializer implements JsonDeserializer<Foo> {
private static final Pattern PATTERN = Pattern.compile("\\$\\{(.*)\\}");
private static Properties props;
private static Map<Class, Converter> converterForClass = new HashMap<>();
static {
try {
Resource resource = new ClassPathResource(String.format("/application-%s.properties", System.getProperty("spring.profiles.active")));
props = PropertiesLoaderUtils.loadProperties(resource);
converterForClass.put(Integer.TYPE, s -> Integer.parseInt(s.replace("\"", "")));
converterForClass.put(Double.TYPE, s -> Double.parseDouble(s.replace("\"", "")));
converterForClass.put(String.class, s -> s);
converterForClass.put(Long.TYPE, s -> Long.parseLong(s.replace("\"", "")));
converterForClass.put(Boolean.TYPE, s -> Boolean.parseBoolean(s.replace("\"", "")));
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public Foo deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
Foo foo = new Foo();
JsonObject jobject = (JsonObject) jsonElement;
for (Entry entry : jobject.entrySet()) {
Field field = searchField(entry.getKey().toString());
if (field != null) {
field.setAccessible(true);
try {
Object r = converterForClass.get(field.getType()).convert(entry.getValue().toString());
field.set(foo, r);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
return foo;
}
private Field searchField(String name) {
Field[] fields = Foo.class.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
if (field.isAnnotationPresent(SerializedName.class)) {
String value = field.getAnnotation(SerializedName.class).value();
Matcher matcher = PATTERN.matcher(value);
if (value.equals(name)) {
return field;
} else if (matcher.find()) {
if (props.get(matcher.group(1)).equals(name)) {
return field;
}
}
} else {
if (field.getName().equals(name)) {
return field;
}
}
}
return null;
}
Register the deserializer:
gsonBuilder.registerTypeAdapter(Foo.class, new FooDeserializer());
The problem with the above approach is it will NOT work with nested objects. You will have to some further verifications and implementation. It is using Java 8 features as well.
Is there a version of BeanUtils.describe(customer) that recursively calls the describe() method on the complex attributes of 'customer'.
class Customer {
String id;
Address address;
}
Here, I would like the describe method to retrieve the contents of the address attribute as well.
Currently, all I have can see the name of the class as follows:
{id=123, address=com.test.entities.Address#2a340e}
Funny, I would like the describe method to retrieve the contents of nested attributes as well, I don't understand why it doesn't. I went ahead and rolled my own, though. Here it is, you can just call:
Map<String,String> beanMap = BeanUtils.recursiveDescribe(customer);
A couple of caveats.
I'm wasn't sure how commons BeanUtils formatted attributes in collections, so i went with "attribute[index]".
I'm wasn't sure how it formatted attributes in maps, so i went with "attribute[key]".
For name collisions the precedence is this: First properties are loaded from the fields of super classes, then the class, then from the getter methods.
I haven't analyzed the performance of this method. If you have objects with large collections of objects that also contain collections, you might have some issues.
This is alpha code, not garunteed to be bug free.
I am assuming that you have the latest version of commons beanutils
Also, fyi, this is roughly taken from a project I've been working on called, affectionately, java in jails so you could just download it and then run:
Map<String, String[]> beanMap = new SimpleMapper().toMap(customer);
Though, you'll notice that it returns a String[], instead of a String, which may not work for your needs. Anyway, the below code should work, so have at it!
public class BeanUtils {
public static Map<String, String> recursiveDescribe(Object object) {
Set cache = new HashSet();
return recursiveDescribe(object, null, cache);
}
private static Map<String, String> recursiveDescribe(Object object, String prefix, Set cache) {
if (object == null || cache.contains(object)) return Collections.EMPTY_MAP;
cache.add(object);
prefix = (prefix != null) ? prefix + "." : "";
Map<String, String> beanMap = new TreeMap<String, String>();
Map<String, Object> properties = getProperties(object);
for (String property : properties.keySet()) {
Object value = properties.get(property);
try {
if (value == null) {
//ignore nulls
} else if (Collection.class.isAssignableFrom(value.getClass())) {
beanMap.putAll(convertAll((Collection) value, prefix + property, cache));
} else if (value.getClass().isArray()) {
beanMap.putAll(convertAll(Arrays.asList((Object[]) value), prefix + property, cache));
} else if (Map.class.isAssignableFrom(value.getClass())) {
beanMap.putAll(convertMap((Map) value, prefix + property, cache));
} else {
beanMap.putAll(convertObject(value, prefix + property, cache));
}
} catch (Exception e) {
e.printStackTrace();
}
}
return beanMap;
}
private static Map<String, Object> getProperties(Object object) {
Map<String, Object> propertyMap = getFields(object);
//getters take precedence in case of any name collisions
propertyMap.putAll(getGetterMethods(object));
return propertyMap;
}
private static Map<String, Object> getGetterMethods(Object object) {
Map<String, Object> result = new HashMap<String, Object>();
BeanInfo info;
try {
info = Introspector.getBeanInfo(object.getClass());
for (PropertyDescriptor pd : info.getPropertyDescriptors()) {
Method reader = pd.getReadMethod();
if (reader != null) {
String name = pd.getName();
if (!"class".equals(name)) {
try {
Object value = reader.invoke(object);
result.put(name, value);
} catch (Exception e) {
//you can choose to do something here
}
}
}
}
} catch (IntrospectionException e) {
//you can choose to do something here
} finally {
return result;
}
}
private static Map<String, Object> getFields(Object object) {
return getFields(object, object.getClass());
}
private static Map<String, Object> getFields(Object object, Class<?> classType) {
Map<String, Object> result = new HashMap<String, Object>();
Class superClass = classType.getSuperclass();
if (superClass != null) result.putAll(getFields(object, superClass));
//get public fields only
Field[] fields = classType.getFields();
for (Field field : fields) {
try {
result.put(field.getName(), field.get(object));
} catch (IllegalAccessException e) {
//you can choose to do something here
}
}
return result;
}
private static Map<String, String> convertAll(Collection<Object> values, String key, Set cache) {
Map<String, String> valuesMap = new HashMap<String, String>();
Object[] valArray = values.toArray();
for (int i = 0; i < valArray.length; i++) {
Object value = valArray[i];
if (value != null) valuesMap.putAll(convertObject(value, key + "[" + i + "]", cache));
}
return valuesMap;
}
private static Map<String, String> convertMap(Map<Object, Object> values, String key, Set cache) {
Map<String, String> valuesMap = new HashMap<String, String>();
for (Object thisKey : values.keySet()) {
Object value = values.get(thisKey);
if (value != null) valuesMap.putAll(convertObject(value, key + "[" + thisKey + "]", cache));
}
return valuesMap;
}
private static ConvertUtilsBean converter = BeanUtilsBean.getInstance().getConvertUtils();
private static Map<String, String> convertObject(Object value, String key, Set cache) {
//if this type has a registered converted, then get the string and return
if (converter.lookup(value.getClass()) != null) {
String stringValue = converter.convert(value);
Map<String, String> valueMap = new HashMap<String, String>();
valueMap.put(key, stringValue);
return valueMap;
} else {
//otherwise, treat it as a nested bean that needs to be described itself
return recursiveDescribe(value, key, cache);
}
}
}
The challenge (or show stopper) is problem that we have to deal with an object graph instead of a simple tree. A graph may contain cycles and that requires to develop some custom rules or requirements for the stop criteria inside the recursive algorithm.
Have a look at a dead simple bean (a tree structure, getters are assumed but not shown):
public class Node {
private Node parent;
private Node left;
private Node right;
}
and initialize it like this:
root
/ \
A B
Now call a describe on root. A non-recursive call would result in
{parent=null, left=A, right=B}
A recursive call instead would do a
1: describe(root) =>
2: {parent=describe(null), left=describe(A), right=describe(B)} =>
3: {parent=null,
{A.parent=describe(root), A.left=describe(null), A.right= describe(null)}
{B.parent=describe(root), B.left=describe(null), B.right= describe(null)}}
and run into a StackOverflowError because describe is called with objects root, A and B over and over again.
One solution for a custom implementation could be to remember all objects that have been described so far (record those instances in a set, stop if set.contains(bean) return true) and store some kind of link in your result object.
You can simple use from the same commom-beanutils:
Map<String, Object> result = PropertyUtils.describe(obj);
Return the entire set of properties for which the specified bean provides a read method.