Custom json (de)serialize with springboot - java

It`s possible to create one JsonSerialize and Deserialize with spring boot?
I put in my appliation.properties this line
spring.jackson.date-format=dd/MM/yyyy HH:mm:ss
but when I return one Date he allways returns a wrong value (yyyy-MM-dd) so I try to create one custom serialization following the http://docs.spring.io/spring-boot/docs/1.4.0.RELEASE/reference/htmlsingle/#boot-features-json-components
but don`t work.
this is my return:
#Entity
#Table(name = "view_atos_praticados", catalog="db_registro", schema="db_wsprefeituraatos")
public class ViewAtosPraticados {
#JsonIgnore
#Id
#Column(name="id")
private Integer id;
#Column(name="descricao_ato")
private String ato;
#JsonIgnore
#Column(name="livro")
private String livro;
#JsonIgnore
#Column(name="numero_ato")
private Integer nrAto;
#JsonIgnore
#Column(name="numero_registro")
private String nrRegistro;
#Column(name="dat_registro")
private Date registro;
#Column(name="ic_transmissao")
private String transmite;

Try #JsonFormat annotation:
#JsonFormat(pattern="dd/MM/yyyy HH:mm:ss")
#Column(name="dat_registro")
private Date registro;

Related

JOOQ MappingException when using multiset select

Hi I am attempting to perform a jooq multiset select to map a one to many relationship into my POJOs.
org.jooq.exception.MappingException: An error ocurred when mapping record to class io.comp.biz.core.task.model.TaskDetails
at org.jooq.impl.DefaultRecordMapper$MutablePOJOMapper.map(DefaultRecordMapper.java:916)
at org.jooq.impl.DefaultRecordMapper.map(DefaultRecordMapper.java:610)
at org.jooq.impl.AbstractRecord.into(AbstractRecord.java:804)
at org.jooq.impl.ResultQueryTrait.fetchOneInto(ResultQueryTrait.java:530)
at io.comp.biz.core.task.query.TaskQueryRepository.findTaskDetailsById(TaskQueryRepository.java:172)
.....
Caused by: org.jooq.exception.DataTypeException: No Converter found for types org.jooq.Converters$UnknownType and java.util.List
at org.jooq.impl.Tools.converterOrFail(Tools.java:1216)
at org.jooq.impl.Tools.converterOrFail(Tools.java:1225)
at org.jooq.impl.AbstractRecord.get(AbstractRecord.java:351)
at org.jooq.impl.DefaultRecordMapper$MutablePOJOMapper.map(DefaultRecordMapper.java:943)
at org.jooq.impl.DefaultRecordMapper$MutablePOJOMapper.map(DefaultRecordMapper.java:873)
... 112 more
These are my POJOS:
#NoArgsConstructor
#AllArgsConstructor
#Builder
#Data
public class TaskDetails {
private UUID id;
private UUID tenantId;
private String title;
private String description;
private LocalDateTime createdAt;
private String priority;
private Long numericId;
private UUID assigneeId;
private UUID statusId;
private UUID reporterId;
private Boolean deleted;
private LocalDateTime modifiedAt;
private UUID modifiedBy;
private List<TaskDate> taskDates;
}
#NoArgsConstructor
#AllArgsConstructor
#Builder
#Data
public class TaskDate {
private UUID id;
private UUID taskId;
private String type;
private LocalDate date;
private LocalDateTime createdAt;
private String createdBy;
private String modifiedBy;
private LocalDateTime modifiedAt;
}
This is my jooq query:
public Optional<TaskDetails> findTaskDetailsById(UUID taskId) {
TaskDetails taskDetails =
dslContext.select(
TASK.ID,
TASK.TENANT_ID,
TASK.TITLE,
TASK.DESCRIPTION,
TASK.CREATED_AT,
TASK.PRIORITY,
TASK.NUMERIC_ID,
TASK.ASSIGNEE_ID,
TASK.STATUS_ID,
TASK.REPORTER_ID,
TASK.DELETED,
TASK.MODIFIED_AT,
TASK.MODIFIED_BY,
multiset(dslContext.select(TASK_DATE.ID,
TASK_DATE .TASK_ID,
TASK_DATE.TYPE,
TASK_DATE.DATE,
TASK_DATE.CREATED_AT,
TASK_DATE.CREATED_BY,
TASK_DATE.MODIFIED_BY,
TASK_DATE.MODIFIED_AT).from(TASK_DATE).where(TASK_DATE.TASK_ID.eq(TASK.ID))).as("taskDates").convertFrom(r -> r.map(mapping(this::mapTaskDate))))
.from(TASK)
.where(TASK.ID.in(taskId)).fetchOneInto(TaskDetails.class);
return Optional.ofNullable(taskDetails);
}
private TaskDate mapTaskDate(UUID id, UUID taskId, String type, LocalDate taskDate, LocalDateTime createdAt, String createdBy, String modifiedBy, LocalDateTime modifiedAt) {
return TaskDate.builder()
.id(id)
.taskId(taskId)
.type(type)
.date(taskDate)
.createdAt(createdAt)
.createdBy(createdBy)
.modifiedBy(modifiedBy)
.modifiedAt(modifiedAt)
.build();
}
spring-boot: (2.6.2) with spring-boot-starter-jooq dependency
jooq version: 3.15.1
From debugging the code i can see that jooq's AbstractRecord.java is throwing the exception when attempting to get the type of taskDates but I am unsure how to resolve this exception
jOOQ added MULTISET support in version 3.15.0, just one patch release before the version that you're using. Since then, there had been a ton of bugfixes in this area, see the release notes. Until version 3.17.6, the release notes reference the word "MULTISET" 65 times. I vaguely recall a bug like this.
For example, there's #12208, which was fixed in jOOQ 3.16.0 and 3.15.3. You should definitely upgrade, at least to the latest 3.15 patch release, possibly even to 3.17.

Spring boot swagger ui make automatically updated fields invisible

I have a Spring Boot application with the following entities:
#Data
#Entity
#Table(name = "jokes")
#EqualsAndHashCode(callSuper = true)
public class Joke extends BaseEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name = "content")
private String content;
}
and
#Data
#MappedSuperclass
public abstract class BaseEntity {
#Column(updatable = false)
#CreationTimestamp
private LocalDateTime createdAt;
#UpdateTimestamp
private LocalDateTime updatedAt;
}
My Controller:
#PostMapping("/jokes")
public Joke createJoke(#Valid #RequestBody Joke joke) {
return jokeRepository.save(joke);
}
The fields createdAt and updatedAt are automatically updated. I am using swagger and when I go on an end point which allows me to create a new resource, then swagger gives me the option of updating the fields createdAt and updatedAt at as well as shown below. The are not actually being updated, but I would like to not see them there. Anyone knows how I could do this?
If you want the fileds to be hidden only at the Swagger level, you can do it with the #Schema annotation of Swagger
#Data
#MappedSuperclass
public abstract class BaseEntity {
#Column(updatable = false)
#CreationTimestamp
#Schema(hidden = true) // One way to do it
private LocalDateTime createdAt;
#UpdateTimestamp
#Schema(accessMode = READ_ONLY) // Other way to do it
private LocalDateTime updatedAt;
}
The difference between the two of them is, hidden will remove the property from Schema component which will make it unavailable for both request and response payloads while accessMode will hide it only for request payloads and not show the access mode in the Schema component as mentioned in the documentation.
accessMode:
Allows to specify the access mode (AccessMode.READ_ONLY, READ_WRITE) AccessMode.READ_ONLY: value will not be written to during a request but may be returned during a response. AccessMode.WRITE_ONLY: value will only be written to during a request but not returned during a response. AccessMode.READ_WRITE: value will be written to during a request and returned during a response.
hidden:
Allows schema to be marked as hidden.
And if you want it to be ignored by the JSON parser that you're using, you'll need something like JsonIgnore if you're using Jackson as the parser.
#Data
#MappedSuperclass
public abstract class BaseEntity {
#Column(updatable = false)
#CreationTimestamp
#JsonIgnore // Jackson will ignore this field
private LocalDateTime createdAt;
#UpdateTimestamp
#JsonIgnore // this will also be ignored
private LocalDateTime updatedAt;
}

Java LocalDate - Time gets appended onto date when saving in MongoDB?

I have the following Java Entity:
public class Round {
private ObjectId _id;
#NotEmpty
#Getter
#Setter
#Accessors(fluent = true)
#JsonProperty("userId")
private String userId;
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy")
#JsonDeserialize(using = LocalDateDeserializer.class)
#JsonSerialize(using = LocalDateSerializer.class)
#Getter
#Setter
#Accessors(fluent = true)
#JsonProperty("date")
private LocalDate date;
//other fields
}
When I do a POST to my Spring Boot REST web app with JSON Body:
{
"userId": "user3",
"date": "20-01-2020"
}
The date is persisted in Mongo as follows:
2020-01-20T00:00:00.000+00:00
How can I get the date to persist as simply:
20-01-2020
It's not Java problem, MongoDB uses Date format similar to JavaScript Date format.
If you want to save just dd-MM-YYYY you may want to change your column type to String.
If it's not possible then you need to rewrite your serializer to return String representation of date (and of course rewrite deserializer to parse that string into LocalDate

Set disallowed fields in Spring Data Rest

I want to exclude certain fields from a POST to my repositories.
For example I want to set the version myself so users cannot set this field themselves.
For example in the class below.
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#CreatedDate
private LocalDateTime created;
#LastModifiedDate
private LocalDateTime lastModified;
private String name;
}
I have tried to use the #ReadOnlyProperty annotation and not having a setter for the version field. But nothing worked, users can still set the version fields themselves. I have also tried to implement a global initializer like below, but without success. The binder gets picked up though.
#ControllerAdvice
public class GlobalInitializer {
#InitBinder
public void globalBinder(WebDataBinder webDataBinder) {
webDataBinder.setDisallowedFields("name");
}
}
You should place #JsonIgnore on field and on setter, and place #JsonProperty("propertyName") on getter.
Just tested - works for me:
#JsonIgnore
#LastModifiedDate
private LocalDate lastUpdated;
#JsonProperty("lastUpdated")
public LocalDate getLastUpdated() {
return lastUpdated;
}
#JsonIgnore
public void setLastUpdated(LocalDate lastUpdated) {
this.lastUpdated = lastUpdated;
}

Only using #JsonIgnore during serialization, but not deserialization

I have a user object that is sent to and from the server. When I send out the user object, I don't want to send the hashed password to the client. So, I added #JsonIgnore on the password property, but this also blocks it from being deserialized into the password that makes it hard to sign up users when they don't have a password.
How can I only get #JsonIgnore to apply to serialization and not deserialization? I'm using Spring JSONView, so I don't have a ton of control over the ObjectMapper.
Things I've tried:
Add #JsonIgnore to the property
Add #JsonIgnore on the getter method only
Exactly how to do this depends on the version of Jackson that you're using. This changed around version 1.9, before that, you could do this by adding #JsonIgnore to the getter.
Which you've tried:
Add #JsonIgnore on the getter method only
Do this, and also add a specific #JsonProperty annotation for your JSON "password" field name to the setter method for the password on your object.
More recent versions of Jackson have added READ_ONLY and WRITE_ONLY annotation arguments for JsonProperty. So you could also do something like:
#JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
private String password;
Docs can be found here.
In order to accomplish this, all that we need is two annotations:
#JsonIgnore
#JsonProperty
Use #JsonIgnore on the class member and its getter, and #JsonProperty on its setter. A sample illustration would help to do this:
class User {
// More fields here
#JsonIgnore
private String password;
#JsonIgnore
public String getPassword() {
return password;
}
#JsonProperty
public void setPassword(final String password) {
this.password = password;
}
}
Since version 2.6: a more intuitive way is to use the com.fasterxml.jackson.annotation.JsonProperty annotation on the field:
#JsonProperty(access = Access.WRITE_ONLY)
private String myField;
Even if a getter exists, the field value is excluded from serialization.
JavaDoc says:
/**
* Access setting that means that the property may only be written (set)
* for deserialization,
* but will not be read (get) on serialization, that is, the value of the property
* is not included in serialization.
*/
WRITE_ONLY
In case you need it the other way around, just use Access.READ_ONLY.
In my case, I have Jackson automatically (de)serializing objects that I return from a Spring MVC controller (I am using #RestController with Spring 4.1.6). I had to use com.fasterxml.jackson.annotation.JsonIgnore instead of org.codehaus.jackson.annotate.JsonIgnore, as otherwise, it simply did nothing.
Another easy way to handle this is to use the argument allowSetters=truein the annotation. This will allow the password to be deserialized into your dto but it will not serialize it into a response body that uses contains object.
example:
#JsonIgnoreProperties(allowSetters = true, value = {"bar"})
class Pojo{
String foo;
String bar;
}
Both foo and bar are populated in the object, but only foo is written into a response body.
"user": {
"firstName": "Musa",
"lastName": "Aliyev",
"email": "klaudi2012#gmail.com",
"passwordIn": "98989898", (or encoded version in front if we not using https)
"country": "Azeribaijan",
"phone": "+994707702747"
}
#CrossOrigin(methods=RequestMethod.POST)
#RequestMapping("/public/register")
public #ResponseBody MsgKit registerNewUsert(#RequestBody User u){
root.registerUser(u);
return new MsgKit("registered");
}
#Service
#Transactional
public class RootBsn {
#Autowired UserRepository userRepo;
public void registerUser(User u) throws Exception{
u.setPassword(u.getPasswordIn());
//Generate some salt and setPassword (encoded - salt+password)
User u=userRepo.save(u);
System.out.println("Registration information saved");
}
}
#Entity
#JsonIgnoreProperties({"recordDate","modificationDate","status","createdBy","modifiedBy","salt","password"})
public class User implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String country;
#Column(name="CREATED_BY")
private String createdBy;
private String email;
#Column(name="FIRST_NAME")
private String firstName;
#Column(name="LAST_LOGIN_DATE")
private Timestamp lastLoginDate;
#Column(name="LAST_NAME")
private String lastName;
#Column(name="MODIFICATION_DATE")
private Timestamp modificationDate;
#Column(name="MODIFIED_BY")
private String modifiedBy;
private String password;
#Transient
private String passwordIn;
private String phone;
#Column(name="RECORD_DATE")
private Timestamp recordDate;
private String salt;
private String status;
#Column(name="USER_STATUS")
private String userStatus;
public User() {
}
// getters and setters
}
You can use #JsonIgnoreProperties at class level and put variables you want to igonre in json in "value" parameter.Worked for me fine.
#JsonIgnoreProperties(value = { "myVariable1","myVariable2" })
public class MyClass {
private int myVariable1;,
private int myVariable2;
}
You can also do like:
#JsonIgnore
#JsonProperty(access = Access.WRITE_ONLY)
private String password;
It's worked for me
I was looking for something similar. I still wanted my property serialized but wanted to alter the value using a different getter. In the below example, I'm deserializing the real password but serializing to a masked password. Here's how to do it:
public class User() {
private static final String PASSWORD_MASK = "*********";
#JsonIgnore
private String password;
#JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
public String setPassword(String password) {
if (!password.equals(PASSWORD_MASK) {
this.password = password;
}
}
public String getPassword() {
return password;
}
#JsonProperty("password")
public String getPasswordMasked() {
return PASSWORD_MASK;
}
}
The ideal solution would be to use DTO (data transfer object)

Categories