I would like to map a field with nested collection using Orika library. My field in class is defined as:
private final List<List<Pojo>> list = new LinkedList<List<Pojo>>();
Pojo is a simple POJO class. Unfortunately I've got a MappingException caused by NullPointerException in Orika's internal logic.
Did I do something in wrong way? Maybe I need to use Custom Mapping feature?
EDIT:
Here is my code:
public class Pojo {
private int field;
public int getField() {
return field;
}
public void setField(final int field) {
this.field = field;
}
}
public class Source {
private final List> list = new LinkedList>();
public List<List<Pojo>> getList() {
return list;
}
}
public class Destination {
private final List> listDest = new LinkedList>();
public List<List<Pojo>> getListDest() {
return listDest;
}
}
public class Main {
public static void main(final String[] args) {
final MapperFactory factory = new DefaultMapperFactory.Builder().build();
factory.classMap(Source.class, Destination.class).field("list", "listDest").byDefault().register();
final Source src = new Source();
final LinkedList<Pojo> nestedList = new LinkedList<Pojo>();
final Pojo pojo = new Pojo();
pojo.setField(8978);
nestedList.add(pojo);
src.getList().add(nestedList);
final MapperFacade facade = factory.getMapperFacade();
final Destination dest = facade.map(src, Destination.class);
System.out.println(dest.getListDest().get(0).get(0).getField());
}
}
Execution above code results this Exception:
Exception in thread "main" ma.glasnost.orika.MappingException: Error encountered while mapping for the following inputs:
rawSource=com.bbh.nested.Source#39185ce6
sourceClass=class com.bbh.nested.Source
destinationClass=class com.bbh.nested.Destination
You can see this Example:
public class ShopEntity {
private Long id;
private String name;
private String logo;
private String url;
private ProductCategory mainCategory;
private Set<ShopRel> shopRels = new HashSet<>(0);
private Account account;
// Assume getter/setter
}
public class ProductCategory extends BaseEntity {
private Long id;
private String name;
// Assume getter/setter
}
public class ShopRel {
private Long id;
private SaleChannel saleChannel;
private Boolean enabled;
// Assume getter/setter
}
public class SaleChannel {
private Long id;
private String name;
private String image;
private String description;
private Boolean active;
// Assume getter/setter
}
public class ShopDto {
private Long id;
private String name;
private String logo;
private String url;
private Long mainCategory;
private Set<ShopRelDto> shopRelDtos = new HashSet<ShopRelDto>();
// Assume getter/setter
}
public class ShopRelDto {
private Long channelId;
private String name;
private Boolean enabled;
// Assume getter/setter
}
public class MapperUtils {
private static final MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
private static final MapperFacade mapper = mapperFactory.getMapperFacade();
static {
mapperFactory.classMap(ShopEntity.class, ShopDto.class)
.field("mainCategory.id", "mainCategory")
.fieldMap("shopRels", "shopRelDtos").aElementType(ShopRel.class).bElementType(ShopRelDto.class).add()
.register();
mapperFactory.classMap(ShopRel.class, ShopRelDto.class)
.field("saleChannel.id", "channelId")
.field("saleChannel.name", "name")
.field("enabled", "enabled")
.register();
}
public static final void map(Object source, Object distance) {
mapper.map(source, distance);
}
public static final <T> T map(Object source, Class<T> destinationClass){
return mapper.map(source, destinationClass);
}
public static void main(String[] args) {
ShopEntity shop = new ShopEntity();
shop.setId(1L);
shop.setName("ABC");
ProductCategory productCategory =new ProductCategory();
productCategory.setId(10L);
shop.setMainCategory(productCategory);
Set<ShopRel> shopRels = new HashSet<>(0);
ShopSaleChannelRel channelRel = new ShopSaleChannelRel();
channelRel.setId(1L);
channelRel.setEnabled(true);
SaleChannel saleChannel = new SaleChannel();
saleChannel.setId(1L);
saleChannel.setName("Channel1");
channelRel.setSaleChannel(saleChannel);
shopRels.add(channelRel);
shop.setShopRels(shopRels);
ShopDto shopDto = map(shop, ShopDto.class);
System.out.println(shopDto);
}
}
It may need a custom mapping via customize if there is lot of cases like this you can extend Orika via Specifications to support this use case
Related
I have 3 class(techspecs, packages, and features) objects where they all share the same fields. The fields are big and instead of repeating setting the fields of each field 3 times(which ends up looking like duplicates), I would like to pass the class objects into one method that uses the generic object to setting the object fields.
I tried passing the class object as a generic but then i dont have access to its members. This is what i tried
Packages packagesFeatures = new Packages();
TechSpecs techSpecsFeature = new TechSpecs();
packagesFeatures = addFeatures(Packages.class, packagesFeatures, vehFeatures);
techSpecsFeature = addFeatures(TechSpecs.class, techSpecsFeature, vehFeatures);
Then
private <T> T addFeatures(Class<T> clazz, T obj, VehicleFeature vehFeatures) {
T inst = null;
try {
inst = clazz.getDeclaredConstructor().newInstance();
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
e.printStackTrace();
}
if (inst instanceof Packages) {
obj = (T) new Packages();
}
if(inst instanceof TechSpecs){
obj = (T) new TechSpecs();
}
if(inst instanceof Features){
obj = (T) new Features();
}
//then somthing like:
//obj.setFeatureId(vehFeatures.getFeatureId());
// obj.setFeatureKey(vehFeatures.getFeatureKey());
// obj.setFeatureCode(vehFeatures.getFeatureCode());
return obj;
EDIT
Each of the 3 classes extend BaseFeatures
public abstract class BaseFeatures {
private String featureId;
private String featureKey;
private String featureCode;
private String subSectionId;
private String subSectionName;
private String featureIdName;
private Integer subSectionRank;
private Integer featureImgClassificationId;
private String featureImgClassification;
private boolean has3DAnimation;
private String sectionId;
private String searchKeys;
private String description;
private String featureName;
private double featureRank;
private String geoId;
private String ecc;
private String specSegments;
private String featureIconType;
private String featureIconText;
private double featureValue;
private boolean standardCertain;
private boolean built;
private List<String> featureKeyAnswers;
private boolean isNumeric;
private boolean adasFeature;
private List<String> icCodeAnswers;
private String featureKeyNoBrand;
private List<StyleInfo> styles;
private List<String> optionCodes;
private List<String> changeOptions;
//getters and setters.
Here is one of the classes.
public class TechSpecs extends BaseFeatures {
private String techSpecs;
public void setTechSpecs(String techSpecs) {
this.techSpecs = techSpecs;
}
public String getTechSpecs(){
return techSpecs;
}
}
All of these fields need to be set in the class object of all 3 classes
EDIT 2
VehicleFeature Class is a standalone class
#JsonInclude(JsonInclude.Include.NON_NULL)
public class VehicleFeature {
private String section;
private String subSection;
private String featureName;
private String subSectionId;
private String sectionName;
private String subSectionName;
If it were me, I would simplify your addFeatures(...) method to something like:
private <T> T addFeatures(Class<T> clazz, BaseFeatures theseFeatures) {
T obj = null;
try {
obj = clazz.getDeclaredConstructor(BaseFeatures.class).newInstance(theseFeatures);
} catch (ReflectiveOperationException roe) {
roe.printStackTrace();
}
return obj;
}
I'd add these two constructors to BaseFeatures:
public abstract class BaseFeatures{
protected String featureId;
protected String featureKey;
protected String featureCode;
/*...*/
protected BaseFeatures(String featureId, String featureKey, String featureCode){
this.featureId = featureId;
this.featureKey = featureKey;
this.featureCode = featureCode;
}
protected BaseFeatures(BaseFeatures features){
this.featureId = features.featureId ;
this.featureKey = features.featureKey;
this.featureCode = features.featureCode;
}
/*...*/
}
You can see how that implementation would actually work, here:
public class BigAssFields {
/* ... */
static public void main(String ... args){
BigAssFields bLike = new BigAssFields();
VehicleFeature vehFeatures = new VehicleFeature("what", "the actual", "Feature");
TechSpecs bigTechSpecs = bLike.addFeatures(TechSpecs.class, vehFeatures);
}
/* ... */
}
public class Call {
private String status;
private String callName;
private int duration;
private int waitedTime;
}
I have a list of calls and i have to create a summary, like this:
public class CallSummary {
private String callName;
private List<ItemSummary> items;
private int averageDuration;
private int averageWaitedTime
}
public class itemSummary {
private String status;
private Integer percentage;
}
My goal is show a summary like :
{
callname:"sac",
averageDuration:"60",
averageWaitedTime:"10",
items:{
status:"failed",
percentage:"40"
status:"answered","60"
}
}
how can i do it using java 8 stream and Collectors ?
I want to parse a YAML-file via Jackson but encounter the problem that one of the properties (let's call it 'Event') has a string called 'type' and a 'properties' object that differs for different Events. My issue is that I need to define the POJOs for this YAML. Therefore, I want to define a Hashmap with VariableObject that can be any of some predefined classes (for brevity, let's say Shipping and Inventory).
How can I implement a Hashmap like that?
public class Event {
private static String type;
private static Map<String, VariableObject> properties;
public static void main(String[] args) {
Inventory inventory = new Inventory("inventoryName", 13);
properties.put("Inventory", inventory);
Shipping shipping = new Shipping("shippingName", true);
properties.put("Shipping", shipping);
}
}
public class Inventory {
private static String name;
private static int someNumber;
public Inventory(String name, int someNumber) {
this.name = name;
this.someNumber = someNumber;
}
}
public class Shipping {
private static String name;
private static boolean someBoolean;
public Shipping(String name, boolean someBoolean) {
this.name = name;
this.someBoolean = someBoolean;
}
}
What you're talking ablut is simple Object. It's the most specific common superclass:
private static Map<String, Object> properties;
Other solution would be to make Inventory and Shipping implement some common interface, for example Named and use it as type parameter in HashMap.
One way to do this is to make Shipping and Inventory implements the same interface (like VariableObject in your cas)
public class Event {
private static String type;
private static Map<String, VariableObject> properties;
public static void main(String[] args) {
Inventory inventory = new Inventory("inventoryName", 13);
properties.put("Inventory", inventory);
Shipping shipping = new Shipping("shippingName", true);
properties.put("Shipping", shipping);
}
}
public interface VariableObject{
//you can define common methods here if you want
}
public class Inventory implements VariableObject{
private static String name;
private static int someNumber;
public Inventory(String name, int someNumber) {
this.name = name;
this.someNumber = someNumber;
}
}
public class Shipping implements VariableObject{
private static String name;
private static boolean someBoolean;
public Shipping(String name, boolean someBoolean) {
this.name = name;
this.someBoolean = someBoolean;
}
}
I have an Arraylist named deleteFields.
I have an Object named mergedDiffSRO.
I have to delete all the fields of mergedDiffSRO which are present in deleteFields.
LeadDetailsSRO mergedDiffSRO = new LeadDetailsSRO();
public class LeadDetailsSRO{
private String emailId;
private String emailByCompany;
private int level;
private LeadObjects leadobj;
private String alternateNumber;
private String languagePreference;
private String kycName;
private String businessAs;
private String aadharName;
private String panName;
private String ovdName;
private String kycStatus;
private String aadhaarStatus;
private String panStatus;
private Set<String> ownershipTypeSet;
private String empId;
private String designation;
private Boolean nameMatchSuccess = null;
private String isSIMandatory;
}
List<String> deleteFields = new ArrayList<String>();
deleteFields.add("businessAs");
deleteFields.add("empId");
deleteFields.add("designation");
deleteFields.add("emailByCompany");
deleteFields.add("level");
deleteFields.add("ovdName");
How do i proceed with the same?
Is reflection to be used for the same?
Please suggest some way out with proper code in JAVA.
You can do it with reflection. But that's ugly, slow and error-prone. So, here it is:
public void deleteFieldsByName(LeadDetailsSRO details, List<String> fieldNames) throws Exception {
for (String fieldName : fieldNames) {
Field field = LeadDetailsSRO.class.getDeclaredField(fieldName);
// this is usually not allowed at production settings
field.setAccessible(true);
Class fieldType = field.getType();
// the following if-else is ugly.
// But that's what we can do. We have to differentiate by classes.
if (fieldType.equals(String.class)) {
field.set(details, null);
} else if (fieldType.equals(Set.class)) {
field.set(details, new HashSet<>());
} else if (fieldType.toString().equals("int")) {
field.set(details, 0);
}
}
I suggest that you look for other kind of solutions.
Update
We can do it without reflection too. This is still ugly and error-prone. But at least, it's fast and it will work in prod environments too:
public class LeadDetailsSRO {
private String emailId;
private String emailByCompany;
private int level;
private String alternateNumber;
private String languagePreference;
private String kycName;
private String businessAs;
private String aadharName;
private String panName;
private String ovdName;
private String kycStatus;
private String aadhaarStatus;
private String panStatus;
private Set<String> ownershipTypeSet;
private String empId;
private String designation;
private Boolean nameMatchSuccess = null;
private String isSIMandatory;
public void deleteFields(List<String> fields) {
for (String fieldName : fields) {
switch (fieldName) {
case "emailId":
this.emailId = null;
break;
case "emailByCompany":
this.emailByCompany = null;
break;
// ...
}
}
}
}
i using follow code:
#XStreamAlias("ListOfMBDO")
public class XMLListOfMBDO {
#XStreamImplicit(itemFieldName = "MBDO")
public List<ModifyBetriebsortDataObject> items = new LinkedList<ModifyBetriebsortDataObject>();
}
public class ModifyBetriebsortDataObject {
#XStreamAlias("PK")
public Integer pk;
#XStreamAlias("NAME")
public String name;
public ModifyBetriebsortDataObject(final Integer pk, final String name) {
this.pk = pk;
this.name = name;
}
}
public void loadThis() {
final String test = "<ListOfMBDO><MBDO><PK>123456</PK><NAME>Test</NAME></MBDO></ListOfMBDO>";
final XStream _xStream = new XStream(new DomDriver());
_xStream.processAnnotations(XMLListOfMBDO.class);
_xStream.processAnnotations(ModifyBetriebsortDataObject.class);
final XMLListOfMBDO testList = (XMLListOfMBDO) _xStream.fromXML(test);
}
The Serializationto XML works fine. But the deserialization throws an
com.thoughtworks.xstream.mapper.CannotResolveClassException: ....data.XMLListOfMBDO : ....data.XMLListOfMBDO
at com.thoughtworks.xstream.mapper.DefaultMapper.realClass(DefaultMapper.java:68)
at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:38)
at com.thoughtworks.xstream.mapper.DynamicProxyMapper.realClass(DynamicProxyMapper.java:71)
...
Why?
You can try solving deserialization error by adding
_xStream.alias("ListOfMBDO", XMLListOfMBDO.class);