Closed faroukelabady closed 8 years ago
Hi Farouk,
Thank you for the support, this encourages me to do better. In regard of your proposals:
<conversion>
return package.Service.conversion(${source});
</conversion>
I need to study ByteBuddy to find another solution.
Hi Alessandro thanks for your reply , and glad that I gave you encouragement :), regarding the points 1- this will be a bug then , because I tried it and the xml or api overrides the annotation. also I followed the code and I found this behavior in source code for jmapper
2- that could be good too , I manager to pass the colelction class type , and return the corresponding object as a result , you can see a sample of that in the source code I attached in the google groups.
3-if I use static annotation then I will be forced to write some bussiness code in the model class, and using thee api or xml will be very limited , we are currently using the jmapper in a spring environment . and we want to achieve something like this
class test {
@Autowired
LookupService service;
public Destination getDestinationBy(int id) {
Source source = em.find(Source.class, id);
Currency currency = service.getCurrecnyByCode(source.getCurrencyCode());
Jmapper<Destination,Source> mapper = new Jmapper<>(Destination.class, Source.class);
Detination d = mapper.getDestinatoin(source);
// can't convert from a code string to currency object need a custom conversion
// but if we do so then the bussiness code will be inside the model class
// doing it manual for now.
d.setCurrency(currency);
return d;
}
}
so as you can see our problem here is the conversion from a currency code string to currency object that is retrieved from another autowired service, it will be good if jmapper can handle those kind of mapping in some ways maybe something like the following // using java8 lambdas mapper.getDestination(source, () -> service.getCurrencyByCode(source.code));
ofcourse that raises a question of how to map this kind of custom relation and how to make jmapper map the correct variable, I have done some manual code for that also attached in the google groups :)
Best regards Farouk Elabady
It is better if we work for single point
For the first point what i mean is that you can define configuration in annotation and conversion methods in XML/API, or viceversa. This mix is provided by the framework, if in your case it does not work, give me a test case on which to work.
let's start with this first
Okay let's revise the example then
public class Dest {
@JMap
String test;
String currency;
}
public class Source {
String test;
String code;
}
public static void main(String[] args) {
Source source = new Source();
source.setCode("USD");
source.setTest("test here");
ModelMapper map = new DefaultModelMapper();
JMapperAPI api = new JMapperAPI();
api.add(JMapperAPI.mappedClass(Dest.class).add(new Attribute("currency").value("code"))
.add(new Conversion("conversion").from("code").to("currency").body(""
+ "return ${source};"))
);
JMapper<Dest, Source> mapper = new JMapper<>(Dest.class, Source.class, api);
Dest d = mapper.getDestination(source);
}
okay in this example I have a destination class , with one variable annotated while the other is mapped using the API or xml , so my prediction here is that Jmapper will be able to read the xml and then combine it with the annotation for the same destination class , and have the final configured class, but what actually happens in code is as follows if class is xml configured then process xml configruation else look for the annotation inside the class
so basically if a class in xml configured it will ignore this class annotation , but if we have two classes one xml configured and one annotated , then it will load one class from the xml and read the other using the annotation.
I do not understand why you need this, why not set up everything in annotation or API / XML? the mix is allowed between configuration and conversion, or between classes (a class configured in the annotation and another in xml), but the same class configured with two formats I do not see the usefulness.
follow your example revisited, classes:
public class Dest {
@JMap String test;
@JMap String currency;
}
public class Source {
String test;
String code;
}
execution code:
...
import static com.googlecode.jmapper.api.JMapperAPI.*;
...
public static void main(String[] args) {
JMapperAPI api = new JMapperAPI().add(
mappedClass(Dest.class).add(
conversion("conversion").from("code").to("currency")
.body("return ${source};")));
Source source = new Source();
source.setCode("USD");
source.setTest("test here");
JMapper<Dest, Source> mapper = new JMapper<>(Dest.class, Source.class, api);
Dest d = mapper.getDestination(source);
}
Classes configured with annotation and conversion in XML, viceversa is permitted too.
What do you think about? could you do me a concrete case where instead there is the need to mix various configuration types for the same class?
For the third point, give the possibility to pass a lambda expression (or an inner class for old java versions) to conversions can be useful.
This is an enhancement that i will develop
including lamdba expression or inner classes in custom conversion will be very good , and will also solve the first point too , as for a concrete case, in my company we are using jmapper annotation and with a custom wrapper class around jmapper we are doing the covnersion between java entity and DTO model , so annotation was very convenient , and easily accessible for all the developers , but we came across an issue , which is now we want to to get a string value from an entity pass it to a rest service and get the required object , e.g ( pass currency code , and get a currency object ) , now the entity will hold a string and the model will hold an object , and we need a custom conversion for that, but if we write the code inside the DTO class then it won't be a pure data class anymore because it will talk to a rest service , and things won't be totally isolated , and trying the custom conversion using xml or api will force me to move all the annotation from this model to the api or xml version , and that won't be very pretty for some classes to be annotated while others are xml configured, it will somehow confuse the developers , on when to use what kind of configuration.
trying the custom conversion using xml or api will force me to move all the annotation from this model to the api or xml version
No, you can define configuration in annotation and ONLY conversion method in API/XML format (see the example above, mappedClass has no attribute definition), you do not need to redefine the relationship in XML.
I have helped you in some way, or I did not understand?
that sounds very good, okay I will try it out once more and inform you if there is anything new, thanks for your help so much :)
ok, i hope that everything works :smiley:
I tried your example but it gives me the following error
Caused by: com.googlecode.jmapper.exceptions.MappingErrorException: incorrect configuration of the currency field in Dest Class: the currency field doesn't exist in Source Class
at com.googlecode.jmapper.config.Error.mapping(Error.java:404)
at com.googlecode.jmapper.config.ConfigReader.getValue(ConfigReader.java:99)
at com.googlecode.jmapper.config.ConfigReader.retrieveTargetFieldName(ConfigReader.java:280)
at com.googlecode.jmapper.operations.OperationHandler.loadStructures(OperationHandler.java:123)
at com.googlecode.jmapper.generation.MapperConstructor.
then I edited the Dest class and added the following
public class Dest {
@JMap String test;
@JMap("code") String currency;
}
but now I get the following error
Caused by: com.googlecode.jmapper.exceptions.ConversionBodyIllegalCodeException: the conversion method contains illegal code, check the conversion code belonging to the Dest class. Additional information: [source error] syntax error near "UTF-8\"?>
<jmapper
"
at com.googlecode.jmapper.config.Error.bodyContainsIllegalCode(Error.java:107)
at com.googlecode.jmapper.generation.JavassistGenerator.generate(JavassistGenerator.java:75)
at com.googlecode.jmapper.generation.MapperGenerator.generateMapperClass(MapperGenerator.java:76)
at com.googlecode.jmapper.generation.MapperBuilder.generate(MapperBuilder.java:88)
at com.googlecode.jmapper.JMapper.createMapper(JMapper.java:458)
at com.googlecode.jmapper.JMapper.
very strange exception I was pretty sure it was working before
please attach the JMapperAPI configuration and the XML obtained from the method call api.toXstream().toString()
sorry for the late reply here is the XML document
<?xml version="1.0" encoding="UTF-8"?>
<jmapper
xmlns="http://jmapper-framework.github.io"
xmlns:xsi="http://jmapper-framework.github.io/jmapper-core"
xsi:noNamespaceSchemaLocation="http://jmapper-framework.github.io/jmapper-core/jmapper-1.6.0.xsd">
<class name = "org.test.jmapper2.Dest">
<conversion name= "conversion" from ="code" to ="currency">return ${source};
</conversion>
</class>
</jmapper>
also here is the code I wrote, maybe I did something wrong
public class Dest {
@JMap
String test;
@JMap("code")
String currency;
public String getTest() {
return test;
}
public void setTest(String test) {
this.test = test;
}
public String getCurrency() {
return currency;
}
public void setCurrency(String currency) {
this.currency = currency;
}
}
public class Source {
String test;
String code;
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getTest() {
return test;
}
public void setTest(String test) {
this.test = test;
}
}
public class Main {
public static void main(String[] args) {
JMapperAPI api = new JMapperAPI().add(
mappedClass(Dest.class).add(
conversion("conversion").from("code").to("currency")
.body("return ${source};")));
System.out.println(api.toXStream().toString());
Source source = new Source();
source.setCode("USD");
source.setTest("test here");
JMapper<Dest, Source> mapper = new JMapper<>(Dest.class, Source.class, api);
Dest d = mapper.getDestination(source);
}
}
Hi Alessandro Vurro, I love you framework and how fast it can get , ans would be great if you can add the following ideas
1 - mix between xml configuration and annotation digging around the code I found that xml configuration takes higher precedence, which is normal ,so what I wish for is the possiblity to mix between the two for a single class for example I have a class called currency that I applied some annotation to it , but I need to make dynamic custom conversion so I needed to do it in xml or api style, but when I do that all the annotation is gone , and I am forced to move all the annotation into the xml , would be great if the two can be applied.
2- mapping between destination and source list , for example JMapper<Dest, Source> mapper = new JMapper<>(Dest.class, Source.class); mapper.getDestinationList(source); or mapper.getDestination(// list of source and return list of destination); it will be also great if I can control the collection type , or maybe return a configured map of key value.
3- using functional API and lambda for custom conversion. to see this through I will apply a simple example with the following code.
as from the example what I want to do is to custom convert between a String code , into a currency object, if I use api or xml , it will be erro prone plus , javaassist complaint a lot tell I got it right , and what if I want the vice versa of the conversion to convert from a destination to a source, so my idea is maybe if we used functional api to supply the custom conversion code then that would be great for example
I will also attach a wrapper class I made to solve this kind of problems , would love your feedback. kindly find the google groups link for the files https://groups.google.com/forum/#!topic/jmapper-framework/m8Dx9uMXsxM
Best regards Farouk Elabady