I'm a junior web developer.
I don't understand why the authenticatedPgInfo return NULL
The follow is my simple test.
#RestController
public class InitController {
#PostMapping("/test")
public String test(#RequestBody AuthenticatedPgInfo authenticatedPgInfo) {
return "";
}
}
And
#Data
public class AuthenticatedPgInfo {
private int code;
private String message;
private List<AuthenticatedPg> result;
}
If I change #Data to #Getter and #Setter, authenticatedPgInfo return null.
But, When I remove #Data, #Getter...(Lombok Annotation) and generate getter and setter, then It works!!
I don't understand why Lombok doens't work in this situation..
Can somebody help?
Thanks in advance.
test.json
{
"result": [
{
"key": "rabbit",
"value": "3"
},
{
"key": "lion",
"value": "1"
}
],
"message": "success",
"code": 0
}
OMG... I Made a biiiiiiiiiig mistake.
I didn't check Enable annotation processingin IntelliJ
All code all works as expected. Thank you guys!
The #Data annotation implicitly includes #RequiredArgsConstructor, but not #NoArgsConstructor. Since Jackson needs a no argument constructor, then you would need to provide one. You can do that with Lombok by simply adding #NoArgsConstructor.
#Data
#NoArgsConstructor
public class AuthenticatedPgInfo {
private int code;
private String message;
private List<AuthenticatedPg> result;
}
Right now I reproduced the same example at my own - with #Data annotation only all works as expected.
To serialize json to the object, Jackson needs the default constructor of this object (i.e. without any parameters) or constructor with parameters and with #JsonCreator annotation.
By default Lombok creates the default constructor if any other does not exists. And Jackson use it. But maybe in your case something goes wrong, so you can create your DTO with #JsonCreator annotation, like this for example:
#Data
public class AuthenticatedPgInfo {
private int code;
private String message;
private List<AuthenticatedPg> result;
#JsonCreator
public AuthenticatedPgInfo(#JsonProperty("code") in code, #JsonProperty("mesage") String message, #JsonProperty("result") List<AuthenticatedPg> result) {
this.code = code;
this.message = message;
this.result = result;
}
}
Related
I am trying to create an immutable DTO.
Therefore I have added the #Builder and #Getter Lombok annotation for creating immutable objects from Pizza.class. To prevent the ingredients field to be initialized with a mutable List, I have added the #Singular Lombok annotation.
DTO
#Builder
#Getter
public class Pizza {
private final String name;
#Singular
private final List<String> ingredients;
}
Now if I create an API endpoint and try to send a pizza JSON to that endpoint, it somehow gets unmarshalled by Spring, but the result of that process is a mutable ingredient list.
Controller
#RestController
#RequestMapping("/api/v1/demo")
public class DemoController {
#PostMapping("/pizza")
Pizza addPizza(#RequestBody Pizza pizza) {
pizza.getIngredients().add("Honey");
return pizza;
}
}
Request/ Response
Request body:
{
"name": "Hawaii",
"ingredients": ["Pineapple"]
}
Response body:
{
"name": "Hawaii",
"ingredients": [
"Pineapple",
"Honey"
]
}
The below code snippet is throwing a java.lang.UnsupportedOperationException, which indicates to me that the ingredients field is an unmodifiable list.
Code snippet
var ingredients = new ArrayList<String>();
ingredients.add("Pinapple");
var pizza2 = Pizza.builder().name("Hawaii").ingredients(ingredients).build();
pizza2.getIngredients().add("Honey");
My questions:
How is Spring Boot doing the marshalling/ unmarshalling of the request body/ response body?
How can I prevent Spring Boot from initializing the ingredients field with a modifiable list?
Your list gets passed to a constructor in the builder, so it's overriding what #Singular is doing here. You can drop the Singular and Builder annotations, create your own builder, and deserialize through it. In the Pizza constructor, the list is made immutable.
#Getter
#JsonDeserialize(builder = Pizza.PizzaBuilder.class)
public static class Pizza {
private final String name;
private final List<String> ingredients;
private Pizza(String name, List<String> ingredients) {
this.name = name;
this.ingredients = Collections.unmodifiableList(ingredients);
}
#JsonPOJOBuilder
#Setter
#Getter
static class PizzaBuilder {
List<String> ingredients;
String name;
PizzaBuilder name(String name) {
this.name = name;
return this;
}
PizzaBuilder ingredients(List<String> ingredients) {
this.ingredients = ingredients;
return this;
}
public Pizza build() {
return new Pizza(name, ingredients);
}
}
}
I use snake_case DB columns and camelCase DTO.
And our team want to use snake_case when we code React component.
Because of it, I added #JsonNaming on DTO. But it works when I send Json data, as you know.
Is there any annotation or setting similar to #JsonNaming?
Here is my postman data and sample codes.
Debug data: sampleName=name, sampleDesc=null.
// Controller
#RestController
#RequestMapping("/sample")
public class SampleController {
#Autowired
private SampleService sampleService;
#GetMapping
public Result getSampleList(SampleDTO param) throws Exception {
return sampleService.getFolderList(param);
}
#PostMapping
public Result insertSample(#RequestBody SampleDTO param) throws Exception {
// this method works well with #JsonNaming
return sampleService.insertFolder(param);
}
}
// DTO
#Setter
#Getter
#NoArgsConstructor
#JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
#Alias("SampleDTO")
public class SampleDTO {
#NotNull
private Long sampleNo;
#NotBlank
private String sampleName;
private String sampleDesc;
#Builder
public SampleDTO(Long sampleNo, String sampleName, String sampleDesc) {
this.sampleNo = sampleNo;
this.sampleName = sampleName;
this.sampleDesc = sampleDesc;
}
}
I had the same problem and didn't find an annotation for this but maybe you can use #ConstructorProperties like this in your DTO's constructor:
#ConstructorProperties({"sample_no","sample_name","sample_desc"})
public SampleDTO(Long sampleNo, String sampleName, String sampleDesc) {
this.sampleNo = sampleNo;
this.sampleName = sampleName;
this.sampleDesc = sampleDesc;
}
I have a Spring Boot app that has basic CRUD services. For the read services I want to see their relations as well. There is no problem for implementing relations by #ManyToOne, #OneToOne, etc. annotations like this example.
My problem is I want to enable this relations based on a parameter in list service or I could use another endpoint as well. How can I achieve this? Any suggestions are welcome.
parameter version could be like ->
/employe/list?includeRelations=true
endpoint version could be like ->
/employee/list/byRelations
My entities are like;
#Entity
#Table(name = "employee")
public class Employee{
private long id;
private String name;
private Address address;
// getter setters
}
#Entity
#Table(name = "address")
public class Address {
private long id;
private String name;
private String postalCode;
// getter setters
}
EDIT
e.g.
without includeRelations=true '/employee/list' service should return this;
{
"id": 1,
"name": "Jane"
}
with includeRelations=true '/employee/list' service should return this;
{
"id": 1,
"name": "Jane"
"address": {
"id":1,
"name": "HOME",
"postalCode": "11111"
}
}
its some sudo code for your understanding . you can use Query Parameter and In Condition you call repo what you want :
for my scenario i want different response short, medium and long
#RequestMapping(value = "/getContacts", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE, headers = "Accept=application/json")
public String getContact(#RequestBody ContactItemRequestInfo contactItemRequestInfo,
#RequestParam(required = false) String key,
String Contact)
{
if(key.equals("medium"))
{
return Contact="{\"responseCode\":\"02\",\"responseDescription\":\"Success\",\"totalCount\":2,\"contacts\":[{\"id\":114,\"firstName\":\"ali\",\"lastName\":\"kamran\"},{\"id\":115,\"firstName\":\"usman\",\"lastName\":\"khan\",\"middleName\":\"saad\"}]}";
}
else if(key.equals("long"))
{
return Contact="{\"responseCode\":\"03\",\"responseDescription\":\"Success\",\"totalCount\":2,\"contacts\":[{\"id\":114,\"firstName\":\"ali\",\"lastName\":\"kamran\"},{\"id\":115,\"firstName\":\"usman\",\"lastName\":\"khan\",\"middleName\":\"saad\"}]}";
}
else
{
return Contact="{\"responseCode\":\"00\",\"responseDescription\":\"Success\",\"totalCount\":2,\"contacts\":[{\"id\":114,\"firstName\":\"ali\",\"lastName\":\"kamran\"},{\"id\":115,\"firstName\":\"usman\",\"lastName\":\"khan\",\"middleName\":\"saad\"}]}";
}
}
It will be helpful for you !!
One of the ways would be to have different data transfer objects to return, depending on the REST request.
Let's assume you have the following classes, apart from entities.
class EmployeeDto {
private Long id;
private String name;
}
class EmployeeAddressDto {
private Long id;
private String name;
private AddressDto address;
}
class AddressDto {
private Long id;
private String name;
private int postalCode;
}
Then in a controller you would do something like this.
#GetMapping("/employee/list")
public ResponseEntity<?> getEmployees(#RequestParam int detailed) {
if (detailed) {
return employeeService.getDetailedEmployeeList();
} else {
return employeeService.getEmployeeList();
}
}
Service inteface would look like this.
interface EmployeService() {
List<EmployeeDto> getEmployeeList();
List<EmployeeAddressDto> getDetailedEmployeeList();
}
You would also need to handle entities to transfer objects conversions.
You can annotate the relations with fetchType=Lazy . Then invoke the getters to manually load the needed relations.
Another option is to eagerly load all relationships, but annotate the response with #JsonView and exclude the relations you don't need.
is it posible to generate a custom "presence checking" method name, being a method of the property itself rather the owning object?
I know I can use hasProperty() methods to check for presence of a value...
https://mapstruct.org/documentation/stable/reference/html/#source-presence-check
but with Optional or JsonNullable (from OpenApi nonullable) that checking method is on the property itself, not on the owning object... :-(
I can map JsonNullable or Optional easyly 'using' or extending a simple custom Mapper
#Mapper
public class JsonNullableMapper {
public <T> T fromJsonNullable(final JsonNullable<T> jsonNullable) {
return jsonNullable.orElse(null);
}
public <T> JsonNullable<T> asJsonNullable(final T nullable) {
return nullable != null ? JsonNullable.of(nullable) : JsonNullable.undefined();
}
}
what I would like to achieve is something like this as "presence check":
if(source.getProperty().isPresent()) {
target.set(customMapper.map(source.getProperty()));
}
Any one found a solution for this?
Thanks and regards
I have managed to implement custom lombok extension which generates "presence checknig" methods.
Here is an example project. In short I added #PresenceChecker annotation and implemented Lombok Javac Annotation handler.
It's possible to use it together with other Lombok annotations:
#Getter
#Setter
public class User {
private String name;
}
#Getter
#Setter
#PresenceChecker
public class UserUpdateDto {
private String name;
}
//MapStruct Mapper interface declaration
#Mapper
public interface UserMapper {
void updateUser(UserUpdateDto dto, #MappingTarget User user);
}
Generated code:
public class User {
private String name;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
public class UserUpdateDto {
private boolean hasName;
private String name;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
this.hasName = true;
}
public boolean hasName() {
return this.hasName;
}
}
//MapStruct Mapper implementation
public class UserMapperImpl implements UserMapper {
#Override
public void updateUser(UserUpdateDto dto, User user) {
if ( dto == null ) {
return;
}
if ( dto.hasName() ) {
user.setName( dto.getName() );
}
}
}
The answer is unfortunately a straight no.
It is not possible in the current version of MapStruct (1.3.1final) and its not on the shortlist for 1.4.0. You could open up an issue on the git repo of MapStruct as feature request.
I am doing Spring Rest Api project with Spring 4.x
This Works:
Controller.java
#PostMapping("newTransaction")
TransactionRequestModel insertNewTransaction(#RequestBody TransactionRequestModel model){
//do something
}
TransactionRequestModel.java
public class TransactionRequestModel {
private int id;
private List<KeyValue> keyValueList;
public TransactionRequestModel(){}
//default constructor
//getter-setter
}
KeyValue.java
public class KeyValue {
String key;
String value;
//default constructor
//setter-getter
}
Request Body Json
{
"id": 1
"keyValueList": [
{
"key": "dummy",
"value": "dummy"
}
]
}
Spring message converter using jackson is working fine.
This Won't:
When i change TransactionRequestModel.java to following (and delete KeyValue.java)
public class TransactionRequestModel {
public class KeyValue {
String key;
String value;
//default constructor
//setter-getter
}
private int id;
private List<KeyValue> keyValueList;
public TransactionRequestModel(){}
//default constructor
//getter-setter
}
means, making KeyValue an inner class, got following error.
org.springframework.http.converter.HttpMessageNotReadableException:
Could not read document: No suitable constructor found for type
[simple type, class
com.example.model.TransactionRequestModel$KeyValue]: can not
instantiate from JSON object (missing default constructor or creator,
or perhaps need to add/enable type information?)
Why?
All the related post in SO mentions the first scenario. I would like to know why this wont work. Please help.
You have to make your inner class static.
public class TransactionRequestModel {
public static class KeyValue {
String key;
String value;
//default constructor
//setter-getter
}
private int id;
private List<KeyValue> keyValueList;
public TransactionRequestModel(){}
//default constructor
//getter-setter
}