I have a class AusgangsrechnungGeneral which has a property "abrechnungsmonat". It is set from JSON via Jackson which has the same property, but defined as "abrechnungsMonat" - which works fine.
Now I need to deserialize the property from
"abrechnungsMonat" -> "abrechnungsmonat"
but serialize it as
"abrechnungsmonat"
Therefore I implemented follwong code:
#JsonFormat(pattern="yyyy-MM")
#JsonDeserialize(using = YearMonthDeserializer.class)
#JsonSetter("abrechnungsMonat")
private YearMonth abrechnungsmonat;
#JsonGetter("abrechnungsmonat")
public YearMonth getAbrechnungsmonat() {
return this.abrechnungsmonat;
}
For other properties this method works fine, but here I got the following Error:
java.lang.IllegalStateException: Conflicting/ambiguous property name definitions (implicit name 'abrechnungsmonat'): found multiple explicit names: [abrechnungsmonat, abrechnungsMonat], but also implicit accessor: [field com.itf.ghost.propartsyncservice.api.rechnung.propartmodel.AusgangsrechnungGeneral#abrechnungsmonat][visible=true,ignore=false,explicitName=false]
I am using Lombok, but as far as I know Lombok will not generate additional getters if they are already defined. This is the class:
#NoArgsConstructor
#AllArgsConstructor
#Data
#ToString(callSuper = true)
#SuperBuilder
#JsonIgnoreProperties(ignoreUnknown = true)
public class AusgangsrechnungGeneral extends Rechnung {
#JsonSetter("leistungsZeitraum")
private String leistungszeitraum;
#JsonGetter("leistungszeitraum")
public String getLeistugnszeitraum() {
return this.leistungszeitraum;
}
private String filePath;
private Boolean isProberechnung;
#JsonFormat(pattern="yyyy-MM-dd")
#JsonDeserialize(using = LocalDateDeserializer.class)
private LocalDate bezahltAm;
#JsonFormat(pattern="yyyy-MM-dd")
#JsonDeserialize(using = LocalDateDeserializer.class)
private LocalDate zahlungErwartetAm;
#JsonFormat(pattern="yyyy-MM")
#JsonDeserialize(using = YearMonthDeserializer.class)
#JsonSetter("abrechnungsMonat")
private YearMonth abrechnungsmonat;
#JsonGetter("abrechnungsmonat")
public YearMonth getAbrechnungsmonat() {
return this.abrechnungsmonat;
}
#JsonSetter("kundenId")
private Long kunde;
#JsonGetter("kunde")
public Long getKunde() {
return this.kunde;
}
#JsonSetter("abls")
private List<AbrechenbareLeistung> abrechenbareLeistungen;
#JsonGetter("abrechenbareLeistungen")
public List<AbrechenbareLeistung> getAbrechenbareLeistungen() {
return this.abrechenbareLeistungen;
}
}
Solved it with:
#JsonFormat(pattern="yyyy-MM")
#JsonAlias("abrechnungsMonat")
#JsonDeserialize(using = YearMonthDeserializer.class)
#JsonSerialize(using = YearMonthSerializer.class)
private YearMonth abrechnungsmonat;
Related
I am trying to use MapStruct for a structure similar to the following:
#Data
public class ClassAEntity {
private int id;
private String name;
private String numT;
private List<ClassBEntity) bs;
}
#Data
public class ClassBEntity {
private int id;
private String name;
private String numT;
private List<Other> oc;
}
#Data
public class ClassA {
private int id;
private String name;
private List<ClassB) bs;
}
#Data
public class ClassB {
private int id;
private String name;
private List<Other> oc;
}
In the interface I have added the following mapping:
ClassAEntity map(ClassA classA, String numT)
I get a warning because it can't map numT to classBEntity.numT and I can't add it with #Mapping in the following way:
#Mapping(source = "numT", target = "bs[].numT")
On the other hand I need to ignore the parameter oc of classBEntity because "Other" object contains classAEntity and forms a cyclic object. (because I use oneToMany JPA). I have tried the following:
#Mapping(target = "bs[].oc", ignore = true)
Thank you for your help
MapStruct does not support defining nested mappings for collections. You will have to define more explicit methods.
For example to map numT into bs[].numT and ignore bs[].oc you'll need to do something like:
#Mapper
public MyMapper {
default ClassAEntity map(ClassA classA, String numT) {
return map(classA, numT, numT);
}
ClassAEntity map(ClassA classA, String numT, #Context String numT);
#AfterMapping
default void setNumTOnClassBEntity(#MappingTarget ClassBEntity classB, #Context String numT) {
classB.setNumT(numT);
}
#Mapping(target = "oc", ignore = "true")
ClassBEntity map(ClassB classB);
}
I am trying to use postman and put these values into the database, but I keep getting an exception.
what im trying to deserialize from postman:
{
"end_date": "2443-11-34 12:43:23",
"start_date": "2443-11-34 12:43:23"
}
The exception that I get:
2020-05-20 10:55:04.572 WARN 4452 --- [nio-8080-exec-2] .w.s.m.s.DefaultHandlerExceptionResolver :
Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot
deserialize value of type `java.time.Instant` from String "2443-11-34 12:43:23": Failed to deserialize
java.time.Instant: (java.time.format.DateTimeParseException) Text '2443-11-34 12:43:23' could not be
parsed at index 10; nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot
deserialize value of type `java.time.Instant` from String "2443-11-34 12:43:23": Failed to deserialize
java.time.Instant: (java.time.format.DateTimeParseException) Text '2443-11-34 12:43:23' could not be parsed at index 10
at [Source: (PushbackInputStream); line: 3, column: 17] (through reference chain:
com.project.rushhour.model.post.AppointmentPostDTO["end_date"])]
appointment entity:
#Entity
#Data
#NoArgsConstructor
#AllArgsConstructor
public class Appointment extends BaseEntity {
#NotNull
#DateTimeFormat(style = "yyyy-MM-dd HH:mm:ss")
#JsonFormat(shape=JsonFormat.Shape.STRING, pattern="yyyy-MM-dd HH:mm:ss")
#JsonDeserialize(using = JacksonInstantDeserializer.class)
private Instant startDate;
#NotNull
#JsonDeserialize(using = JacksonInstantDeserializer.class)
#DateTimeFormat(style = "yyyy-MM-dd HH:mm:ss")
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
private Instant endDate;
My appointmentDto class:
#Data
#NoArgsConstructor
#AllArgsConstructor
public abstract class AppointmentDTO extends BaseDTO {
#JsonProperty("start_date")
private Instant startDate;
#JsonProperty("end_date")
private Instant endDate;
My AppointmentGetDTO class that I use
public class AppointmentGetDTO extends AppointmentDTO {
}
I also have all of the jackson dependencies
My custom deserializer that I use:
public class JacksonInstantDeserializer extends StdDeserializer<Instant> {
public JacksonInstantDeserializer() { this(null); }
public JacksonInstantDeserializer(Class<?> clazz) { super(clazz); }
#Override
public Instant deserialize(JsonParser parser, DeserializationContext ctx) throws IOException {
return Instant.parse(parser.getText());
}
}
You should create a CustomDeserializer for your class AppointmentGetDTO and not Instant class.
We need to register the AppointmentDTO class with the Deserializer. Below I have provided relevant code changes. User debugger and create breakpoints in the deserializer to test the conversion logic.
For further reading checkout: jackson-deserialization and this Stackoverflow answer
Read this for alternate approach: JsonComponent
AppointmentDTO:
#Data
#NoArgsConstructor
#AllArgsConstructor
public abstract class AppointmentDTO {
#JsonProperty("start_date")
private Instant startDate;
#JsonProperty("end_date")
private Instant endDate;
}
AppointmentGetDTO:
#JsonDeserialize(using= JacksonInstantDeserializer.class)
public class AppointmentGetDTO extends AppointmentDTO {
public AppointmentGetDTO(Instant s, Instant e) {
super(s,e);
}
}
Custom Deserializer for AppointmentGetDTO
public class JacksonInstantDeserializer extends StdDeserializer<AppointmentGetDTO> {
public JacksonInstantDeserializer() { this(AppointmentDTO.class); }
public JacksonInstantDeserializer(Class<?> clazz) { super(clazz); }
#Override
public AppointmentGetDTO deserialize(JsonParser parser, DeserializationContext ctx) throws IOException {
JsonNode node = parser.getCodec().readTree(parser);
Instant s=null;
Instant e=null;
if(node.get("start_date") != null) {
s=Instant.parse(node.get("start_date").asText());
}
if(node.get("end_date")!=null) {
e=Instant.parse(node.get("end_date").asText());
}
return new AppointmentGetDTO(s,e);
}
}
Then you can create bean of com.fasterxml.jackson.databind.Module like below:
#Bean
public Module dynamoDemoEntityDeserializer() {
SimpleModule module = new SimpleModule();
module.addDeserializer(AppointmentGetDTO.class, new JacksonInstantDeserializer());
return module;
}
Controller Mapping for the request:
#PostMapping
public AppointmentDTO convert(#RequestBody AppointmentGetDTO appointmentDTO) {
System.out.println(appointmentDTO.getStartDate());
System.out.println(appointmentDTO.getEndDate());
return appointmentDTO;
}
request json:
{
"end_date": "2443-11-12T12:43:23Z",
"start_date": "2443-11-12T12:43:23Z"
}
I have the QueueContent class that it has is a superclass of two others.
I get a String in JSON format that contains the information I need to extract. The super class is:
#Data
#JsonIgnoreProperties(ignoreUnknown = true)
public class QueueContent {
private String empresa;
private String empresa_cor;
private String empresa_contato;
private String empresa_url;
private String empresa_telefone;
private String empresa_idioma;
public QueueContent(String empresa, String empresa_cor, String empresa_contato, String empresa_url, String empresa_telefone, String empresa_idioma) {
this.empresa = empresa;
this.empresa_cor = empresa_cor;
this.empresa_contato = empresa_contato;
this.empresa_url = empresa_url;
this.empresa_telefone = empresa_telefone;
this.empresa_idioma = empresa_idioma;
}
public QueueContent() {
}
}
I'm using Lombok to generate Getters / Setters)
This is the child class:
#Data
public class EmailCameraOffline extends QueueContent {
private Timestamp camera_last_online;
private String camera_nome;
private String empresa_url_plataforma;
public EmailCameraOffline(String empresa, String empresa_cor, String empresa_contato, String empresa_url, String empresa_telefone, String empresa_idioma, Timestamp camera_last_online, String camera_nome, String empresa_url_plataforma) {
super(empresa, empresa_cor, empresa_contato, empresa_url, empresa_telefone, empresa_idioma);
this.camera_last_online = camera_last_online;
this.camera_nome = camera_nome;
this.empresa_url_plataforma = empresa_url_plataforma;
}
public EmailCameraOffline() {
}
}
So I've done:
EmailCameraOffline infosEmail = new ObjectMapper().readValue(content, EmailCameraOffline.class);
System.out.println(infosEmail);
And the output is:
EmailCameraOffline (camera_last_online = 2020-03-12 03: 01: 45.0, camera_nome = Pier Cam 1, empresa_url_platform = null)
How do I get my EmailCameraOffline object to have the superclass attributes initialized?
Everything should be loaded and initialized just fine, so calling:
System.out.println(infosEmail.getEmpresa());
should give expected value.
Problem
The problem is in the default implementation of toString() method (done via #Data) at EmailCameraOffline class, which does not include inherited fields.
Solution
To fix this you can "override" #Data's toString() implementation to include inherited fields as well using Lombok as:
#Data
#ToString(callSuper = true)
public class EmailCameraOffline extends QueueContent {
...
}
I have this code which I would like to use to translate keys and return data to front end:
#GetMapping("pages")
public Page<ContractDTO> pagxes(#RequestParam(value = "page") int page, #RequestParam(value = "size") int size) {
return contractService.findAll(page, size)
//.map(mapper::toDTO);
.map(g -> new ContractDTO(g.getName(), getMerchantName(g.getMerchant_id())));
}
private String getMerchantName(int id) {
Optional<Merchants> obj = merchantService.findById(id);
return obj.get().getName();
}
DTO :
public class ContractDTO {
private Integer id;
.....
private Integer acquirer_id;
private Integer terminal_id;
private String merchant_id;
......
}
How I can rewrite this code .map(g -> new ContractDTO(g.getName(), getMerchantName(g.getMerchant_id()))); to translate from int to String using getMerchantName(int id) only terminal_id and merchant_id and all other variables not to be translated?
I can create constructor in ContractDTO but the code will be huge. Is there some other way?
Error:
The method builder() is undefined for the type ContractDTO
In your case because you want to avoid multiple constructors, You can use a builder design pattern, by using lombok library, it can be more easier, so you can just annotate your class of ContractDTO with this library annotation, and you have every thing to go :
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#Builder(toBuilder = true)
class ContractDTO {
private Integer id;
private String name;
private Integer acquirerId;
private Integer terminalId;
private String merchantId;
}
then your code can be :
...
.map(g -> ContractDTO.builder()
.name(g.getName())
.merchantName(g.getMerchantId())
.build()
)....
Many times I'm faced with a class which constructor method must contain list of arguments that is identical with the list of class instance variables.
As you see in the example there is "SOME" code to make this hapend.
I'm wondering how can I make this process less painful?
Example:
public class VimeoUser extends Schema {
#Getter #Setter private String uri;
#Getter #Setter private String name;
#Getter #Setter private String link;
#Getter #Setter private String location;
#Getter #Setter private String bio;
#Getter #Setter private String createdTime;
#Getter #Setter private String account;
#Getter #Setter private Map<String,Integer> statistics = new HashMap<>();
#Getter #Setter private List<Website> websites = new ArrayList<>();
#Getter #Setter private List<Portrait> portraits = new ArrayList<>();
public VimeoUser(
String uri,
String name,
String link,
String location,
String bio,
String createdTime,
String account,
Map<String,Integer> statistics,
List<Website> websites,
List<Portrait> portraits){
this.uri = uri;
this.name = name;
this.link = link;
this.location = location;
this.bio = bio;
this.createdTime = createdTime;
this.account = account;
this.statistics = statistics;
this.websites = websites;
this.portraits = portraits;
}
}
It is possible to use a pattern named Builder. It is explained in this question
Basically it works as following:
Create an inner static class Builder
Create a private constructor that take as an argument an object of type Builder
In the Builder class add methods that set a single value and returns this (current reference to instance of the Builder class)
In the body of the constructor of your class use the values passed in the Builder to set each property
add a method build in the Builder that calls the private constructor of your class
Here is an example:
public class NutritionalFacts {
private int sodium;
private int fat;
private int carbo;
public class Builder {
private int sodium;
private int fat;
private int carbo;
public Builder(int s) {
this.sodium = s;
}
public Builder fat(int f) {
this.fat = f;
return this;
}
public Builder carbo(int c) {
this.carbo = c;
return this;
}
public NutritionalFacts build() {
return new NutritionalFacts(this);
}
}
private NutritionalFacts(Builder b) {
this.sodium = b.sodium;
this.fat = b.fat;
this.carbo = b.carbo;
}
}
and to use it do the following:
NutritionalFacts nutritionalFacts = new NutritionalFacts.Builder()
.fat(200).carbo(50).build();
Using this pattern instead of pojo with setter and getter is useful because it is possible to use it also to build immutable objects (objects with all final fields). An immutable object is useful if you need to share it on a multithreaded environment because it is not necessary to synchronize the access to it.
Additionally it is possible to add some controls in the build method to be sure that all fields are setted as expected.
I guess writing pojos for database modelling does not necessarily needs constructor other than default no-arg constructor. If anyway required in some situations, Getters and setters can be used.
Builder pattern
If you want create a object with more readable way, you can use a simple builder pattern. Lombok support this such as #Getter or #Setter. You just add #Builder annotation and everything should works fine.
#Getter
#Builder
public class SomeClass {
private final String valueOne;
private final String valueTwo;
}
And then you can create object in this way.
SomeClass someClass = SomeClass.builder()
.valueOne("one")
.valueTwo("two")
.build();
Fluent accessors method
Alternative way to create a class is using #Accessors annotation with fluent = true. Then you can create a empty object and set the value what you needed in simple way.
#Getter
#Setter
#Accessors(fluent = true)
public class SomeClass {
private String valueOne;
private String valueTwo;
}
Simple sample using this way.
SomeClass someClass = new SomeClass()
.valueOne("one")
.valueTwo("two");
I see you are already using Lombok. There is a #AllArgsConstructor class-level annotation that will generate the constructor for you. If you want the default constructor, too, use #NoArgsConstructor additionally.
More info on the constructor-generating annotations here.