Spring WebClient convert to object with a constructor - spring-webclient

I know that I can map an object to a body by passing a class to bodyToMono(), like this
WebClient.RequestHeadersSpec<?> headersSpec =
bodySpec.body(Mono.just(myRequest), MyRequest.class);
MyResponse test = headersSpec.exchangeToMono(s -> {
if (s.statusCode().equals(HttpStatus.OK)) {
return s.bodyToMono(MyResponse.class);
} else {
return s.createException().flatMap(Mono::error);
}
}).block();
But for it to work I need a no-arg constructor and setters and I don't want setters, but only getters.
My question:
is there any way to make an object from a response by passing a constructor
something like this
return s.bodyToMono(new MyResponse(val1, val2, val3));

Just found out that it is possible if you add
#JsonProperty("property_name") to your constructor parameters
Update
So let's say I have this code excerpt
MyDto myDto = headersSpec.exchangeToMono(s -> {
if (s.statusCode().equals(HttpStatus.OK)) {
return s.bodyToMono(MyDto.class);
}
<...>
})
MyDto.java looks like this
public class MyDto implements Serializable {
private final String val1;
private final Val2 val2;
private final boolean val3;
private final Integer val4;
public MyDto(#JsonProperty("val1") String val1,
#JsonProperty("val2") val12 val2,
#JsonProperty("val3") boolean val3,
#JsonProperty("val4") Integer val4) {
this.val1 = val1;
this.val2 = val2;
this.val3 = val3;
this.val4 = val4;
}
<...getters...>
<... no setters...>
}
So, i.e., val1 from a response is mapped to a val1 (in constructor) by adding a #JsonProperty("val1") next to a val1

Related

Return the built Enum to JSON

I have an object and an enum for it. When I give away an object, I want my enum inside the object to be displayed as an object with the name and value attributes without using DTO, or to be partially used. I want json to build this object itself (enum with name and value), and I give only the object in which this enum is contained.
public enum MyType {
TT("Time Tu"), TD("Time dust");
MyType(String value) {
this.value = value;
}
private String value;
public String getValue() {
return value;
}
#Override
public String toString() {
return value;
}
Here is the DTO, it may be necessary (get/set/constructor auto generated by Intellij Idea)
public class MyTypeWrapper {
private String name;
private String value;
}
#Entity
public class MyObject {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
private String number;
........
private MyType myType;
........
}
Perhaps serialization/deserialization is needed? How to do it?
It should go something like this:
{
.....
myType: {
"name: "TT",
"value: "TD"
},
.....
}
Perhaps this is a piece of the solution? But I'm not sure that it will work, and it's not clear how to serialize
public enum MyType {
......
#JsonValue
private MyTypeWrapper getWrapper()
return new MyTypeWrapper(this.name, this.value)
}
......
}
This turned out to be the solution
public enum MyType {
......
#JsonValue
private MyTypeWrapper getWrapper()
return new MyTypeWrapper(this.name, this.value)
}
......
}

Sending #Value annotated fields to a DTO layer returns null

I have a class which is composed of 2 different objects :
public class MyClass{
private OptionClass optionClass;
private ConstantClass constantClass;
public DocumentToSignRestRequest(OptionClass optionClass, ConstantClass constantClass) {
this.optionClass= optionClass;
this.constantClass= constantClass;
}
}
My first class is a classic POJO. My second class retrieve values from the application.properties file.
public class ConstantClass {
#Value("${api.url}")
private String hostName;
#Value("${sign.path}")
private String pathStart;
public ConstantClass () {
this.hostName= getHostName();
this.path = getPath();
}
I map MyClass with MyClassDto in order to call a service.
#PostMapping(
value="/sign",
consumes = { MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE },
produces = { MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE }
)
public MyClassRest prepareDocument(#RequestBody DocumentToPrepare documentToPrepare) throws Exception {
MyClassRest returnValue = new MyClassRest ();
ModelMapper modelMapper = new ModelMapper();
MyClassDto myClassDto = modelMapper.map(documentToPrepare, MyClassDto .class);
DocumentDto signedDocument = documentService.signDocument(documentDto);
returnValue = modelMapper.map(signedDocument, DocumentRest.class);
return returnValue;
}
My DTO class work fine and retrieve the OptionClass datas, but concerning the second Class, i obtain null as value, while i try to print it out in the service layer.
Your ConstantClass should be a Bean or a Component (as #cassiomolin says in comments)
#Component
public class ConstantClass {
private String hostName;
private String pathStart;
public ConstantClass (#Value("${api.url}") String url, #Value("${sign.path}") String path ) {
this.hostName = url;
this.pathStart = path;
}
// getters...
Then you can easily inject this component in your Controller and use it.
#Controller
public class YourController(){
private ConstantClass constantClass;
public YourController(ConstantClass constantClass){
this.constantClass = constantClass;
}
#PostMapping("...")
public MyClass post(.....){
.....
MyClass myclass = new MyClass(this.constantClass,...)
.....
}
}
note that Spring can autowire #Value and #Component, ... via the constructor; that can be very useful when you do unit-testing

MapStruct: mapping from object with a list of complex object

Supposing I have the following classes:
public class A {
private String id;
private List<B> related;
}
public class B {
private String id;
private String name;
}
public class ADTO {
private String id;
private List<BDTO> relations;
}
public class BDTO {
private String identificator;
private String relatedName;
}
How can I create a mapper that given an A object type returns me an ADTO object with all the information? I have to create two different mappers? Can it be done in only one mapper? I think it would be something like the following, but I don't know how to map the atributtes from the list:
#Mapper
public interface MyMapper {
#Mappings({ #Mapping(source = "related", target = "relations") })
ADTO mapperA(A obj);
}
Thanks in advance.
try this (not tested but should work properly)
when you mapping lists you should make a map for both the class element and the list to map all the elements of the list)
#Mapper
public interface MyMapper {
#Mappings({ #Mapping(source = "related", target = "relations") })
ADTO mapperA(A obj);
#Mappings(
{ #Mapping(source = "id", target = "identificator") },
{ #Mapping(source = "name", target = "relatedName") })
BDTO bDTOMapping(B b);
List<BDTO> bDTOListMapping(List<B> bList);
}

Spring Boot marshall Xml from RestTemplate without RootElement

I am using a RestTemplate like this:
return this.getForEntity(baseUrl, BasicResponse.class, parameters);
This is the BasicResponse class:
public class BasicResponse {
private String status;
private String statusMsg;
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getStatusMsg() {
return statusMsg;
}
public void setStatusMsg(String statusMsg) {
this.statusMsg = statusMsg;
}
}
No exceptions are thrown but the fields in the returned ResponseEntity body are 'null'. I think it's because the element does not have a valid XML structure (as in no root element). I do not have control over the parsed XML. How can I map my object?
Since the XML is not valid,
I believe that you will not be able to use RestTemplate.getForEntity
to get a BasicResponse object.
Try this:
private static final String VALUE_END_TAG = "</blammy>";
private static final String VALUE_START_TAG = "<blammy>";
private XmlMapper xmlMapper; // initialize this correctly, somewhere off page.
method stuff
{
final String actualResponse;
final StringBuilder correctedResponse = new StringBuilder();
final BasicResponse returnValue;
actualResponse = restTemplate.getForEntity(baseUrl, BasicResponse.class, parameters);
correctedResponse.append(VALUE_START_TAG);
correctedResponse.append(actualResponse);
correctedResponse.append(VALUE_END_TAG);
returnValue = xmlMapper.readValue(correctedResponse.toString(), BasicResponse.class);
return returnValue;
}
Use some reasonable value as the element name in the start and end tags,
perhaps "" and "".
Consider using some Jackson annotations,
for example #JacksonXmlRootElement(localName = "blammy")
(this local name matches my example).

Spring #Requestbody not mapping to inner class

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
}

Resources