NOTE: This is unlike other questions on StackOverflow because they resolve this issue by mapping the two classes manually. Since ScheduleSource and ScheduleTarget are exactly the same classes, I want them to be mapped automatically.
Hi,
I have 2 classes ScheduleSource and ScheduleTarget. They have exactly the same properties.
When I try to use MapStruct to map from ScheduleSource to ScheduleTarget, I get the error:
Can't map property "java.util.Optional<java.time.LocalDate> startDate" to "java.time.LocalDate startDate". Consider to declare/implement a mapping method: "java.time.LocalDate map(java.util.Optional<java.time.LocalDate> value)
I have attached the two files. Can you please help?
Files are:
ScheduleSource, ScheduleTarget - the two Java Beans
ScheduleMapper - the mapping class.
ScheduleMapper.java
package testStructMap;
import org.mapstruct.*;
import org.mapstruct.factory.*;
#Mapper
public interface ScheduleMapper {
ScheduleMapper INSTANCE = Mappers.getMapper( ScheduleMapper.class );
ScheduleTarget scheduleSourceToScheduleTarget(ScheduleSource scheduleSource);
}
ScheduleSource.java, ScheduleTarget.java - same structure
package testStructMap;
import java.time.LocalDate;
import java.time.LocalTime;
import java.util.Optional;
import javax.validation.constraints.*;
public class ScheduleSource {
#FutureOrPresent
#NotNull
private LocalDate startDate;
#NotBlank
private String repeatType;
#Positive
private Integer occurrences;
public Optional<LocalDate> getStartDate() {
return Optional.ofNullable(startDate);
}
public void setStartDate(LocalDate startDate) {
this.startDate = startDate;
}
public String getRepeatType() {
return repeatType;
}
public void setRepeatType(String repeatType) {
this.repeatType = repeatType;
}
public Optional<Integer> getOccurrences() {
return Optional.ofNullable(occurrences);
}
public void setOccurrences(Integer occurrences) {
this.occurrences = occurrences;
}
}
In 1.3.0.beta1 the following is supported:
package testStructMap;
import org.mapstruct.*;
import org.mapstruct.factory.*;
#Mapper
public interface ScheduleMapper {
ScheduleMapper INSTANCE = Mappers.getMapper( ScheduleMapper.class );
ScheduleTarget scheduleSourceToScheduleTarget(ScheduleSource scheduleSource);
default <T> T unwrapOptional(Optional<T> optional) {
return optional.orElse(null);
}
}
I'm not familiar with mapstruct, but I can guess it maps different objects :)
If your source and target classes have the same structure then the problem is
public Optional<LocalDate> getStartDate();
public void setStartDate(LocalDate startDate);
So it gets the Optional object and tries to pass it to a method accepting a LocalDate.
So your possible ways of action are
change getter to return a simple object
change setter to accept an optional (which is fine I guess, but
seems a bit off)
declare a mapper method
Map target Optional to source which is not Optional how can we make it follow the example below:
#Named("HelperClass")
class Helper {
#Named("convertToOptional")
public Optional<KontaktpersonVerwandtschaftsgradTyp> convertToOptional(KontaktpersonVerwandtschaftsgradTyp optional) {
return optional != null ? Optional.of(optional) : Optional.empty();
};
}
#Mapping(target = "kontaktpersonVerwandtschaftsgrad", source = "tdfFall.kontaktpersonVerwandtschaftsgrad", qualifiedByName = { "HelperClass",
"convertToOptional" })
and also we need to add uses and the name of the Helper class
#Mapper(componentModel = "spring", uses = { Helper.class })
Related
I'm using a subType property in Jackson, and I want to using this property when deserializing json.
package com.gaosoft.ai.kg.commons.sphinx.strategy;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.sankuai.ai.kg.commons.sphinx.model.FAQRecord;
import com.sankuai.ai.kg.commons.sphinx.model.FAQRequest;
import org.springframework.beans.factory.BeanFactory;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
property = "strategyType"
)
#JsonSubTypes({
#JsonSubTypes.Type(value = StrategyEmpty.class, name = "empty"),
#JsonSubTypes.Type(value = StrategyNormal.class, name = "normal"),
#JsonSubTypes.Type(value = StrategyDummy.class, name = "dummy")
}
)
public abstract class Strategy implements Serializable {
private String strategyName;
private String strategyType;
private Map<String, Object> args = new HashMap<>();
public String getStrategyType() {
return strategyType;
}
public void setStrategyType(String strategyType) {
this.strategyType = strategyType;
}
public Map<String, Object> getArgs() {
return args;
}
public void setArgs(Map<String, Object> args) {
this.args = args;
}
public String getStrategyName() {
return strategyName;
}
public void setStrategyName(String strategyName) {
this.strategyName = strategyName;
}
public abstract void init(BeanFactory beanFactory);
public abstract List<FAQRecord> fetchFAQ(FAQRequest request);
}
Like my code says, there are 3 subtype of abstract class Strategy, and I want to retain the subclass type name in strategyType property.
Is there a way to fill strategyType when using jackson in this way?
(Sorry about my poor English)
I think what you're asking for is the #JsonTypeInfo#visible property:
Note on visibility of type identifier: by default, deserialization (use during reading of JSON) of type identifier is completely handled by Jackson, and is not passed to deserializers. However, if so desired, it is possible to define property visible = true in which case property will be passed as-is to deserializers (and set via setter or field) on deserialization.
So in your example,
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
property = "strategyType",
visible = true
)
That said, this seems like a design smell. Is it truly valid that you can set a StrategyEmpty's strategyType to dummy? If not, and StrategyEmpty should always have a strategyType of empty, then why not just have an abstract getStrategyType() that each subclass implements with a hardcoded value?
With the following Java code:
public class Bean{
private String value;
public Bean(#NonNull String value) {
//Usually fail-fast validation can be added here if it is needed
this.value = value;
}
public String getValue() {return this.value;}
}
Is it possible to check the constructor argument value by means of the annotation, #NonNull at run time other than compile time? Personally I still did not find any checker-framework, which can do validation checking at run time. However, is it possible to implement an Annotation processor to do run time checking?
You should take a look at #NotNull from javax.validation.constraints.
I use it in my models and it throw a Constraint exception when I try to save a model with a null #NotNull value.
The import is import javax.validation.constraints.NotNull;
If you are using Spring and mongodb, you'll have to configure it so it works, I have found a piece of code somewhere on the Internet (can't remember where), you may use it:
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.core.mapping.event.ValidatingMongoEventListener;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
#Configuration
public class CustomRepositoryRestConfigurerAdapter {
#Bean
public LocalValidatorFactoryBean localValidatorFactoryBean() {
return new LocalValidatorFactoryBean();
}
#Bean
public ValidatingMongoEventListener validatingMongoEventListener(
#Qualifier("localValidatorFactoryBean") LocalValidatorFactoryBean lfb
) {
return new ValidatingMongoEventListener(lfb);
}
}
Yes. Lombok's #NonNull is a runtime check which just inserts an if-statement with a throw:
With Lombok
import lombok.NonNull;
public class NonNullExample extends Something {
private String name;
public NonNullExample(#NonNull Person person) {
super("Hello");
this.name = person.getName();
}
}
Vanilla Java
import lombok.NonNull;
public class NonNullExample extends Something {
private String name;
public NonNullExample(#NonNull Person person) {
super("Hello");
if (person == null) {
throw new NullPointerException("person is marked #NonNull but is null");
}
this.name = person.getName();
}
}
Misconception at your end: there is no single answer to your question.
Some annotations, when used on source code like this are mainly targeting compile time. Like some static analysis tool that analyses the data flow to tell you that you are violating "annotated" contracts here or there.
But some annotations are also "meant" to be used at runtime, for example to be used with "beans". Such objects might come in as parameter of a HTTP request, and then you have some framework checking if the content received as JSON for example is actually valid, according to the rules specified via annotations. See this tutorial for some examples.
I have the following class and enums:
import lombok.Data;
// other imports...
#Data
public class MapTest{
private MyFirstEnum myFirstEnum;
private MySecondEnum mySecondEnum;
}
public enum MyFirstEnum{
MY_FIRST_ENUM1,
MY_FIRST_ENUM2
}
public enum MySecondEnum {
MY_SECOND_ENUM1,
MY_SECOND_ENUM2
}
and this spring controller:
#PostMapping("/testMap")
#ResponseBody
public void TestMap(#RequestBody MapTest mapTest){
}
Since an enum can be looked up by its name what I would like to do is to post a json to the controller and that the appropriate props will be serialized by their name:
{
"myFirstEnum": "MY_FIRST_ENUM1",
"mySecondEnum": "MY_SECOND_ENUM2"
}
I've tried to set up a #JsonDeserialize but i couldn't get the type of the enum inside the overridden function:
// what type should i use here?
public static class StringToEnum extends JsonDeserializer<???> {
// how do i get the type of the current enum?
#Override
public ??? deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
??? res = Enum.valueOf(p.getText());
return res;
}
}
Update:
I've failed to mention that i'm using lombok's #data attribute for automatically generating getters and setters, which doesn't work well with enum bindings (not sure why).
I guess that laziness comes with a price.
It should be serialized automatically via jackson but you can force it via #JsonCreator
Redefine your enums as
public enum MyFirstEnum{
MY_FIRST_ENUM1,
MY_FIRST_ENUM2;
#JsonCreator
public static MyFirstEnum fromString(String raw) {
return MyFirstEnum.valueOf(raw.toUpperCase());
}
}
Similarly define your second enum as well in the similar manner.
Imp Note (Mandatory)
MapTest should have public setter / getter for both enums (if declared private, preferred), or declare them public (should be avoided, not preferred)
Currently I have been thinking about the question however, I couldn't find a proper answer. Use case at hand is to create an implementation class for a particular custom annotation so that in the runtime I can simply generate it instead of a POJO.
For instance:
Annotation:
#interface CustomAnnotation {
String date();
}
At this stage I need a bean which happens to have the same fields as the annotation. Here I have two options either implement the annotation and create it in runtime or create a class to carry the information.
A)
Implementation of Annotation:
public class CustomAnnotationImpl implements CustomAnnotation {
private final String date;
public CustomAnnotationImpl(String date) {
this.date = date;
}
#Override
public String date() {
return this.date;
}
#Override
public Class<? extends Annotation> annotationType() {
return CustomAnnotation.class;
}
}
B)
public class CustomBean {
private final String date;
public CustomAnnotationImpl(String date) {
this.date = date;
}
public String getDate() {
return this.date;
}
}
Also keep in my mind that the bean and annotation will be always in sync meaning that bean actually will be always a copy of the annotation.
My question is that what would be the advantages and drawbacks of those, if any? I'm asking this because simply I haven't seen implementation of annotation myself.
I do not understand 100% your question, but it looks like other people already ask something like this.
Use cases for implementing annotations
Use cases for java annotation and more
I use Spring/Spring Boot and Spring MVC with #RestController
I have a composite model objects:
public abstract class BaseQuery {
private final Long characteristicId;
...
}
public abstract class ComparableQuery extends BaseQuery {
private final Object value;
private final String comparisonOperator;
...
}
public class GreaterOrEqualQuery extends ComparableQuery {
public GreaterOrEqualQuery(Long characteristicId, Object value) {
super(characteristicId, value, ">=");
}
}
public class EqualQuery extends ComparableQuery {
public EqualQuery(Long characteristicId, Object value) {
super(characteristicId, value, "=");
}
}
public class GreaterQuery extends ComparableQuery {
public GreaterQuery(Long characteristicId, Object value) {
super(characteristicId, value, ">");
}
}
public class CompositQuery extends BaseQuery {
private final String operator;
private final BaseQuery[] queries;
public CompositQuery(Long characteristicId, Operator operator, BaseQuery... queries) {
super(characteristicId);
this.operator = operator.value;
this.queries = queries;
}
...
}
etc.
The sample usage of this model looks for example like:
Set<BaseQuery> queries = new HashSet<>();
BaseQuery megapixelCharacteristicQuery = new CompositQuery(megapixelCharacteristic.getCharacteristicId(), CompositQuery.Operator.AND, new GreaterOrEqualQuery(megapixelCharacteristic.getCharacteristicId(), 10), new LessOrEqualQuery(megapixelCharacteristic.getCharacteristicId(), 50));
queries.add(megapixelCharacteristicQuery);
queries.add(new EqualQuery(androidCharacteristic.getCharacteristicId(), true));
Serialized JSON object for Set<BaseQuery> queries looks like:
[
{
"operator":"AND",
"queries":[
{
"value":10,
"comparisonOperator":"\u003e\u003d",
"characteristicId":391
},
{
"value":50,
"comparisonOperator":"\u003c\u003d",
"characteristicId":391
}
],
"characteristicId":391
},
{
"value":true,
"comparisonOperator":"\u003d",
"characteristicId":383
}
]
I have to pass this or similar JSON from client application(AngularJS) to my back end REST API endpoint in order to get correctly deserialized model like described above(Set with appropriate entries like CompositQuery or EqualQuery).
Right now my Spring application back end logic at my Rest controller unable to correctly deserialize this JSON with appropriate classes.
Is any way in Spring to provide some meta information(or something else) to this JSON in order to help Spring correctly deserialize this structure ?
You can achieve this using jackson annotations on the superclass like following:
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonSubTypes.Type;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
#JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "javaclass")
#JsonSubTypes({
#Type(value = GreaterOrEqualQuery.class),
#Type(value = EqualQuery.class)
//and so on...
})
public abstract class BaseQuery {
...
}
This will add javaclass property into json representation which is a fully-qualified name in case of use = JsonTypeInfo.Id.CLASS. In order to simplify the value of this property consider different options for use parameter of #JsonTypeInfo (JsonTypeInfo.Id.NAME for example).