java modelMapper: Mapping a Collection to an Object - java

After a lot of testing and research on this topic I cannot fully solve my issue. I'm using modelmapper for entity/DTO mapping in a springboot application. I'm trying to configure my modelmapper to map a Set to a simple DTO object. I already create the a custom converter and it is working as expected:
Converter<Set<CategoryTl>, CategoryTlDTO> converter = new AbstractCustomConverter<Set<CategoryTl>, CategoryTlDTO>() {
#Override
protected D convert(S source, MappingContext<Set<CategoryTl>, CategoryTlDTO> context) {
HashMap<String, CategoryTlDetailsDTO> map = new HashMap<>();
source.forEach(
categoryTl -> map.put(categoryTl.getCatalogLanguage().getLanguage().getCode(),
new CategoryTlDetailsDTO(categoryTl.getName(), categoryTl.getDescription()))
);
return new CategoryTlDTO(map);
}
};
My issue is now to apply this converter to all "Set => CategoryTlDTO". In nested object or not. I try to create a a new TypeMap but I cannot do it because of the "Set" collection.
mapper.createTypeMap(Set<CategoryTl>.class (-> not possible), CategoryTlDTO.class).setConverter(converter);
If I add the converter directly in the model mapper it is just not working.
mapper.addConverter(converter);
Do you have any hint or a solution for this? Maybe I miss something regarding TypeToken and TypeMap Inheritance.
Best regards,

I've not used ModelMapper, but the docs suggest you can use a TypeToken
Type setType = new TypeToken<Set<CategoryTl>>() {}.getType();
mapper.createTypeMap(setType, CategoryTlDTO.class).setConverter(converter);

Related

Serialize/Deserialize complex referenced objects with Jackson

I am currently working on a java project where I need to save multiple objects whith some circular references. I decided to go with jackson but I am really lost because what I want to do seems too complex (hopefully not for you ;) )
I am probably going to be using custom serializer/deserializers but I need your expertise on how I could do that.
For a quick skeleton of the classes whose objects I want to save :
public class Box {
BoxModel bm;
ArrayList<Plug> ins, outs;
// more properties
}
public class Plug{
Box box;
ArrayList<Plug> after;
Plug before;
ArrayList<Wire> wireConnectedAfter;
Wire wireConnectedBefore;
// more properties
}
public class Wire {
Plug plug1, plug2;
// more properties
}
Every object is referenced in my main class (MySketch) in a dedicated ArrayList :
ArrayList<Plug> plugs = new ArrayList<>();
ArrayList<Plug> inputPlugs = new ArrayList<>();
ArrayList<Plug> outputPlugs = new ArrayList<>();
ArrayList<BoxModel> boxModels = new ArrayList<>();
ArrayList<Box> boxes = new ArrayList<>();
ArrayList<Wire> wires = new ArrayList<>();
The goal for me would be to save all these ArrayLists into one json file like the one following (which I achieved) and be able to deserialize them again into the ArrayList of objects which I have no idea how to do.
{
"boxes": [
(all Box objects)
],
"wires": [
(all Wire objects)
],
"plugs": [
(all Plug objects)
]
}
For saving I did something like that:
File save = new File("data/test/test0.json");
ObjectMapper mapper = new ObjectMapper();
Map<String, Object> map = new HashMap<String, Object>();
map.put("boxModels", sketch.boxModels);
map.put("boxes", sketch.boxes);
map.put("wires", sketch.wires);
map.put("plugs", sketch.plugs);
map.put("inputPlugs", sketch.inputPlugs);
map.put("outputPlugs", sketch.outputPlugs);
mapper.writeValue(save, map);
For now, my JSON Loader looks like :
ObjectMapper mapper = new ObjectMapper();
Map<String, Object> content = mapper.readValue(f, new TypeReference<>() {});
ArrayList<Box> boxes = mapper.convertValue(content.get("boxes"), new TypeReference<ArrayList<Box>>(){});
But I get an error :
Exception in thread "main" java.lang.IllegalArgumentException: Cannot construct instance of `src.Box` (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
Thank you for any piece of information you can give me !

Gson: how to include partly serialized object?

Given the class:
class Container<T> {
T item;
String type;
Map<String,String> properties;
public void setItem(T _item) {
item = _item;
}
}
I have already the item serialized in a database as string with the name serialized. It is a Map<String,String>.
I don't know how to say Gson that this variable is already serialized.
So when I use Gson I first deserialize it, then serialize it back
Container<Map <String, String>> t = new Container<>(<other parameters>);
Map <String, String> m = gson.fromJson(serialized, new TypeToken<Map<String,String>>(){}.getType())
t.setItem(m);
gson.toJson(t, new TypeToken<Container<Map<String,String>>>() {}.getType());
This feels inefficient. How do I fix this?
I'm not sure that's possible. You're mixing object creation and serialization.
What you can do is create a new constructor with an additional String parameter and deserialize the string to get your item and set it automatically. That should be possible even with a parameterized type. That way you have 2 lines of code instead of 4.

Parse list of dynamic classes in gson android

I have a collection of classes, such as this:
entityClasses = new HashMap<String, Class>();
entityClasses.put("EntityType1", EntityType1.class);
entityClasses.put("EntityType2", EntityType2.class);
I also have a JSON list of their instances as well:
String entityJSON = "[{"type":"EntityType1","name":"... attributes"},...]";
Where the type attribute will determine the class of the object that will be the target of JSON parsing. How can I parse these using gson?
I tried with the following:
String type = "EntityType1"; // I already can fetch this.
final Class entityClass = entityClasses.get(type);
new Gson().fromJson(entityJSON, new TypeToken<ArrayList<entityClass>>(){}.getType());
Which would work if entityClass was an actual class name, and not a variable that represents a class. In my case however, I get the following error:
Unknown class: 'entityClass'
So how is it possible to parse by a Class variable?
Thanks in advance!
In general, you can't do that since you should pass a class as a generic type: not List<String.class>, but List<String>.
But you can use a workaround like this: store list TypeToken's instead of list generic types:
entityClasses = new HashMap<String, TypeToken>();
entityClasses.put("EntityType1", new TypeToken<ArrayList<EntityType1>>(){});
...
String type = "EntityType1"; // I already can fetch this.
final TypeToken typeToken= entityClasses.get(type);
new Gson().fromJson(entityJSON, typeToken.getType());

Get list of attribute values from list of beans

I have a list of beans and want to obtain a list of values (given a specific attribute).
For instance, I have a list of document definitions and I want to get a list of codes:
List<DDocumentDef> childDefs = MDocumentDef.getChildDefinitions(document.getDocumentDef());
Collection<String> childCodes = new HashSet<String>();
for (DDocumentDef child : childDefs) {
childCodes.add(child.getCode());
}
Is there any more compact solution? Reflection, anonymous inner classes ...?
Thanks in advance
I feel fine with your current approach.
But if you want to add a library (e.g. apache commons-collection and commons-beanutils) or you already have added it, you can do it this way:
// create the transformer
BeanToPropertyValueTransformer transformer = new BeanToPropertyValueTransformer("code" );
// transform the Collection
Collection childCodes = CollectionUtils.collect( childDefs , transformer );
the Guava lib from google provides a similar api.
There is no other way to do this with standard Java API. However, if you are comfortable, you can use Guava's Lists.transform method:
Function<DDocumentDef, String> docToCodes =
new Function<DDocumentDef, String>() {
public String apply(DDocumentDef docDef) {
return docDef.getCode();
}
};
List<String> codes = Lists.transform(childDefs, docToCodes);
Or, wait until Java 8 is out, and then you can use lambdas and streams for this:
List<DDocumentDef> childDefs = ...
List<String> childCodes = childDefs.stream()
.map(docDef -> docDef.getCode())
.collect(Collectors.toCollection(HashSet::new));
Now it's upto you to decide, which one do you prefer, and which one do you think is shorter.

How to convert a Java object (bean) to key-value pairs (and vice versa)?

Say I have a very simple java object that only has some getXXX and setXXX properties. This object is used only to handle values, basically a record or a type-safe (and performant) map. I often need to covert this object to key value pairs (either strings or type safe) or convert from key value pairs to this object.
Other than reflection or manually writing code to do this conversion, what is the best way to achieve this?
An example might be sending this object over jms, without using the ObjectMessage type (or converting an incoming message to the right kind of object).
Lots of potential solutions, but let's add just one more. Use Jackson (JSON processing lib) to do "json-less" conversion, like:
ObjectMapper m = new ObjectMapper();
Map<String,Object> props = m.convertValue(myBean, Map.class);
MyBean anotherBean = m.convertValue(props, MyBean.class);
(this blog entry has some more examples)
You can basically convert any compatible types: compatible meaning that if you did convert from type to JSON, and from that JSON to result type, entries would match (if configured properly can also just ignore unrecognized ones).
Works well for cases one would expect, including Maps, Lists, arrays, primitives, bean-like POJOs.
There is always apache commons beanutils but of course it uses reflection under the hood
Code generation would be the only other way I can think of. Personally, I'd got with a generally reusable reflection solution (unless that part of the code is absolutely performance-critical). Using JMS sounds like overkill (additional dependency, and that's not even what it's meant for). Besides, it probably uses reflection as well under the hood.
This is a method for converting a Java object to a Map
public static Map<String, Object> ConvertObjectToMap(Object obj) throws
IllegalAccessException,
IllegalArgumentException,
InvocationTargetException {
Class<?> pomclass = obj.getClass();
pomclass = obj.getClass();
Method[] methods = obj.getClass().getMethods();
Map<String, Object> map = new HashMap<String, Object>();
for (Method m : methods) {
if (m.getName().startsWith("get") && !m.getName().startsWith("getClass")) {
Object value = (Object) m.invoke(obj);
map.put(m.getName().substring(3), (Object) value);
}
}
return map;
}
This is how to call it
Test test = new Test()
Map<String, Object> map = ConvertObjectToMap(test);
Probably late to the party. You can use Jackson and convert it into a Properties object. This is suited for Nested classes and if you want the key in the for a.b.c=value.
JavaPropsMapper mapper = new JavaPropsMapper();
Properties properties = mapper.writeValueAsProperties(sct);
Map<Object, Object> map = properties;
if you want some suffix, then just do
SerializationConfig config = mapper.getSerializationConfig()
.withRootName("suffix");
mapper.setConfig(config);
need to add this dependency
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-properties</artifactId>
</dependency>
When using Spring, one can also use Spring Integration object-to-map-transformer. It's probably not worth adding Spring as a dependency just for this.
For documentation, search for "Object-to-Map Transformer" on http://docs.spring.io/spring-integration/docs/4.0.4.RELEASE/reference/html/messaging-transformation-chapter.html
Essentially, it traverses the entire object graph reachable from the object given as input, and produces a map from all primitive type/String fields on the objects. It can be configured to output either:
a flat map: {rootObject.someField=Joe, rootObject.leafObject.someField=Jane}, or
a structured map: {someField=Joe, leafObject={someField=Jane}}.
Here's an example from their page:
public class Parent{
private Child child;
private String name;
// setters and getters are omitted
}
public class Child{
private String name;
private List<String> nickNames;
// setters and getters are omitted
}
Output will be:
{person.name=George, person.child.name=Jenna,
person.child.nickNames[0]=Bimbo . . . etc}
A reverse transformer is also available.
With Java 8 you may try this :
public Map<String, Object> toKeyValuePairs(Object instance) {
return Arrays.stream(Bean.class.getDeclaredMethods())
.collect(Collectors.toMap(
Method::getName,
m -> {
try {
Object result = m.invoke(instance);
return result != null ? result : "";
} catch (Exception e) {
return "";
}
}));
}
JSON, for example using XStream + Jettison, is a simple text format with key value pairs. It is supported for example by the Apache ActiveMQ JMS message broker for Java object exchange with other platforms / languages.
Simply using reflection and Groovy :
def Map toMap(object) {
return object?.properties.findAll{ (it.key != 'class') }.collectEntries {
it.value == null || it.value instanceof Serializable ? [it.key, it.value] : [it.key, toMap(it.value)]
}
}
def toObject(map, obj) {
map.each {
def field = obj.class.getDeclaredField(it.key)
if (it.value != null) {
if (field.getType().equals(it.value.class)){
obj."$it.key" = it.value
}else if (it.value instanceof Map){
def objectFieldValue = obj."$it.key"
def fieldValue = (objectFieldValue == null) ? field.getType().newInstance() : objectFieldValue
obj."$it.key" = toObject(it.value,fieldValue)
}
}
}
return obj;
}
Use juffrou-reflect's BeanWrapper. It is very performant.
Here is how you can transform a bean into a map:
public static Map<String, Object> getBeanMap(Object bean) {
Map<String, Object> beanMap = new HashMap<String, Object>();
BeanWrapper beanWrapper = new BeanWrapper(BeanWrapperContext.create(bean.getClass()));
for(String propertyName : beanWrapper.getPropertyNames())
beanMap.put(propertyName, beanWrapper.getValue(propertyName));
return beanMap;
}
I developed Juffrou myself. It's open source, so you are free to use it and modify. And if you have any questions regarding it, I'll be more than happy to respond.
Cheers
Carlos
You can use the java 8 stream filter collector properties,
public Map<String, Object> objectToMap(Object obj) {
return Arrays.stream(YourBean.class.getDeclaredMethods())
.filter(p -> !p.getName().startsWith("set"))
.filter(p -> !p.getName().startsWith("getClass"))
.filter(p -> !p.getName().startsWith("setClass"))
.collect(Collectors.toMap(
d -> d.getName().substring(3),
m -> {
try {
Object result = m.invoke(obj);
return result;
} catch (Exception e) {
return "";
}
}, (p1, p2) -> p1)
);
}
You could use the Joda framework:
http://joda.sourceforge.net/
and take advantage of JodaProperties. This does stipulate that you create beans in a particular way however, and implement a specific interface. It does then however, allow you to return a property map from a specific class, without reflection. Sample code is here:
http://pbin.oogly.co.uk/listings/viewlistingdetail/0e78eb6c76d071b4e22bbcac748c57
If you do not want to hardcode calls to each getter and setter, reflection is the only way to call these methods (but it is not hard).
Can you refactor the class in question to use a Properties object to hold the actual data, and let each getter and setter just call get/set on it? Then you have a structure well suited for what you want to do. There is even methods to save and load them in the key-value form.
The best solution is to use Dozer. You just need something like this in the mapper file:
<mapping map-id="myTestMapping">
<class-a>org.dozer.vo.map.SomeComplexType</class-a>
<class-b>java.util.Map</class-b>
</mapping>
And that's it, Dozer takes care of the rest!!!
Dozer Documentation URL
There is of course the absolute simplest means of conversion possible - no conversion at all!
instead of using private variables defined in the class, make the class contain only a HashMap which stores the values for the instance.
Then your getters and setters return and set values into and out of the HashMap, and when it is time to convert it to a map, voila! - it is already a map.
With a little bit of AOP wizardry, you could even maintain the inflexibility inherent in a bean by allowing you to still use getters and setters specific to each values name, without having to actually write the individual getters and setters.
My JavaDude Bean Annotation Processor generates code to do this.
http://javadude.googlecode.com
For example:
#Bean(
createPropertyMap=true,
properties={
#Property(name="name"),
#Property(name="phone", bound=true),
#Property(name="friend", type=Person.class, kind=PropertyKind.LIST)
}
)
public class Person extends PersonGen {}
The above generates superclass PersonGen that includes a createPropertyMap() method that generates a Map for all properties defined using #Bean.
(Note that I'm changing the API slightly for the next version -- the annotation attribute will be defineCreatePropertyMap=true)
You should write a generic transformation Service! Use generics to keep it type free (so you can convert every object to key=>value and back).
What field should be the key? Get that field from the bean and append any other non transient value in a value map.
The way back is pretty easy. Read key(x) and write at first the key and then every list entry back to a new object.
You can get the property names of a bean with the apache commons beanutils!
If you really really want performance you can go the code generation route.
You can do this on your on by doing your own reflection and building a mixin AspectJ ITD.
Or you can use Spring Roo and make a Spring Roo Addon. Your Roo addon will do something similar to the above but will be available to everyone who uses Spring Roo and you don't have to use Runtime Annotations.
I have done both. People crap on Spring Roo but it really is the most comprehensive code generation for Java.
One other possible way is here.
The BeanWrapper offers functionality to set and get property values (individually
or in bulk), get property descriptors, and to query properties to determine if they
are readable or writable.
Company c = new Company();
BeanWrapper bwComp = BeanWrapperImpl(c);
bwComp.setPropertyValue("name", "your Company");
If it comes to a simple object tree to key value list mapping, where key might be a dotted path description from the object's root element to the leaf being inspected, it's rather obvious that a tree conversion to a key-value list is comparable to an object to xml mapping. Each element within an XML document has a defined position and can be converted into a path. Therefore I took XStream as a basic and stable conversion tool and replaced the hierarchical driver and writer parts with an own implementation. XStream also comes with a basic path tracking mechanism which - being combined with the other two - strictly leads to a solution being suitable for the task.
With the help of Jackson library, I was able to find all class properties of type String/integer/double, and respective values in a Map class. (without using reflections api!)
TestClass testObject = new TestClass();
com.fasterxml.jackson.databind.ObjectMapper m = new com.fasterxml.jackson.databind.ObjectMapper();
Map<String,Object> props = m.convertValue(testObject, Map.class);
for(Map.Entry<String, Object> entry : props.entrySet()){
if(entry.getValue() instanceof String || entry.getValue() instanceof Integer || entry.getValue() instanceof Double){
System.out.println(entry.getKey() + "-->" + entry.getValue());
}
}
By using Gson,
Convert POJO object to Json
Convert Json to Map
retMap = new Gson().fromJson(new Gson().toJson(object),
new TypeToken<HashMap<String, Object>>() {}.getType()
);
We can use the Jackson library to convert a Java object into a Map easily.
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.6.3</version>
</dependency>
If using in an Android project, you can add jackson in your app's build.gradle as follows:
implementation 'com.fasterxml.jackson.core:jackson-core:2.9.8'
implementation 'com.fasterxml.jackson.core:jackson-annotations:2.9.8'
implementation 'com.fasterxml.jackson.core:jackson-databind:2.9.8'
Sample Implementation
public class Employee {
private String name;
private int id;
private List<String> skillSet;
// getters setters
}
public class ObjectToMap {
public static void main(String[] args) {
ObjectMapper objectMapper = new ObjectMapper();
Employee emp = new Employee();
emp.setName("XYZ");
emp.setId(1011);
emp.setSkillSet(Arrays.asList("python","java"));
// object -> Map
Map<String, Object> map = objectMapper.convertValue(emp,
Map.class);
System.out.println(map);
}
}
Output:
{name=XYZ, id=1011, skills=[python, java]}
Add Jackson library
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.6.3</version>
</dependency>
Then you could generate map using Object mapper.
ObjectMapper objectMapper = new ObjectMapper();
Map<String, Object> map = objectMapper.convertValue(
yourJavaObject,
new TypeReference<Map<String, Object>>() {}
);

Categories

Resources