Pageable with nested list spring boot - java

I'm using a pageable object to return an object ResponseDto which contains a list of record
the object is in this form:
public class ResponseDto{
private String prop1;
private String prop2;
private List<NestedDto> nestedDto;
}
i can get with 2 different queries both Page<ResponseDto> and List<NestedDto> but i have to get inside the pageable content and update it to create the nested object.
EDIT: More info
so in what i'm doing is:
Page<ResponseDto> response = repository.findRecord()
and
List<NestedDto> nested = otherRepository.findNestedRecord(someProp)
so it will be something like this
for(ResponseDto el: response.getContent()){
el.setNestedDto(otherRepository.findNestedRecord(el.getSomeProp));
}
Is there a more efficient way to create this?

I guess you haven't done anything wrong with this implementation.
{
"prop1": "anyProp1",
"prop2": "anyProp2",
"nestedDto": [
{
"attr1": "anyAttr1",
"attr2": "anyAttr2"
},
{
"attr1": "anyAttr1"
"attr2": "anyAttr2"
}
]
}
Maybe naming can change but it is pseudo so it is not important.
Also you can sort and filter by (nestedDto.attr1 or nestedDto.attr2)

Related

Automatic deserialization from String to object for #RequestBody in Spring

I am having issues with serialization and as much as I have been looking around, I am not able to find a solution to this.
I have inside my #RestController something similar to this method for and endpoint:
public ResponseEntity<String>(#RequestBody RequestObject requestObject )
The RequestObject looks like this:
public class RequestObject {
private Driver driver;
private List<Tracks> tracks;
//constructors, getters and setters
}
public class Tracks {
private Enum1 name;
private List<Enum2> missions;
//constructors, getters and setters
}
So, the problem comes with the Listof Enum2. This is because what I receive from the JSON is not a list of strings, but rather a string, that I need to parse to convert to the values of Enum2
So Enum2 looks like this:
A,
B,
C;
And what I get from the request as a JSON is:
{
"driver" : {
"name" : "myname"
}
},
"tracks" : [
{
"name" : "madrid",
"missions" : "ABCCBA"
},
{
"name" : "barcelona",
"mission" : "CBBCA"
},
]
}
Thing is all deserialization works perfectly out of the box (including all nested enums) except for the List<Enum2> missions
I have managed to do a dirty trick, but I'd like to do it the proper way, which I understand would be to write some kind of deserializer that runs when the controller is fired and that does all that parsing and converting from the string to the list (this splitting code I do have it, but I don't know where to put it).
At the moment I am getting this
2021-09-13 21:02:34.924 WARN 99904 --- [nio-8080-exec-8] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type `java.util.ArrayList<org.package.model.Enum2>` from String value (token `JsonToken.VALUE_STRING`); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize value of type `java.util.ArrayList<org.model.Enum2>` from String value (token `JsonToken.VALUE_STRING`)
at [Source: (PushbackInputStream); line: 13, column: 26] (through reference chain: org.model.RequestObject["missions"]->java.util.ArrayList[0]-org.model.Track["missions"])]
Is there a way to do this at all?
I found the solution. I had to mark Track as follows:
#JsonDeserialize(using = TrackDeserializer.class)
public class Track
and the write the TrackDeserializer:
public class TrackDeserializer extends JsonDeserializer<Track>{
#Override
public Track deserialize(JsonParser j, DeserializationContext ctxt) throws IOException{
JsonNode jsonNode = j.getCodec().readTree(jsonParser);
String name = jsonNode.get("name").asText();
final List<Enum2> enum = parseEnums(jsonNode.get("mission").asText());
return new Track(name, enum);
}
And parseEnums does the splitting and conversion to list.
Now TrackDeserializer is called for every Track with the given JSON in the question.
Probably, the simplest solution without using any Jackson annotation would be to use a custom setter method:
private void setMissions(String value) {
this.missions = convert_value_to_list_of_enums;
}
Jackson will invoke this method with values: ABCCBA and CBBCA.

Dynamic JSON Generation in Java

I have to send a batch of instructions in JSON data format as below:
{
"batchId": "123456",
"instructions": [ {
"instructionId": "1",
"customer": {
"iban": "abc",
"name": "abc"
}
},
"instructionId": "2",
"customer": {
"iban": "abc",
"name": "abc"
}
}
.
.
.
.. . . ..
]
}
Now, i will fetch multiple records/instructions from the database via query, which I will use to generate the JSON data-set as per the format above.
What I have researched: I have traversed many solutions and thought of my own that I shall create an object class of Instruction mapping with setters and getters. I shall use a loop through each record and set values of object fields from database via setter and add entry that complete object entry to JSON via getters.
I will continue this approach until the records are read completely.
My question is: Is this solution efficient and best one to deal with such requirement or any other suggestion?
Please suggest? Any sort of code snippet or help is appreciated.
You can use com.google.gson for that.
Create the Instruction object and then add the fields and annotate the field with SerializedName if you are mapping a different json name to your java field(E.g. instruction_id to instructionId).
import com.google.gson.annotations.SerializedName;
import java.util.List;
import java.util.Map;
public class Instruction {
#SerializedName("instructionId")
private String instructionId;
#SerializedName("customer")
private List<Map<String, Object>> customer;
public String getInstructionId() {
return instructionId;
}
public void setInstructionId(String instructionId) {
this.instructionId = instructionId;
}
public List<Map<String, Object>> getCustomer() {
return customer;
}
public void setCustomer(List<Map<String, Object>> customer) {
this.customer = customer;
}
}
And then you can map your json to your instruction class.
Instruction instruction = new GsonBuilder()
.setPrettyPrinting()
.create()
.fromJson(json, Instruction.class);

Spring data util pair rename json name

my first post here. I'm working on a API and i found a trouble that i don´t know how to solve it.
I´m trying to get the remaining stock of all products in my database. I´m using Spring boot and MongoDB with the Spring Data dependency.
Here is my code:
#GetMapping("/remaining-stock")
public ResponseEntity<List<Pair<String, Integer>>> showAllStock() throws EmptyDepositException{
List<Pair<String, Integer>> allStock;
try {
allStock = depServ.showAllStock();
}catch(EmptyDepositException ex) {
allStock = null;
}
return ResponseEntity.ok(allStock);
}
When i do that GET request, this is the JSON i get:
[
{
"first": "Water",
"second": 5
},
{
"first": "Milk",
"second": 40
}
]
The values are OK but i want to rename the variables names with a better name like this:
[
{
"Product name": "Water",
"Remaining stock": 5
},
{
"Product name": "Milk",
"Remaining stock": 40
}
]
There is a way to do that?
Sorry for my english, i'm from Argentina so maybe something is not clear. I hope you can help me.
Thank you all in advance.
You can use #JsonProperty annotation which indicates that the field name is used as the property name without any modifications, but it can be specified to non-empty value to specify different name. Property name refers to name used externally, as the field name in JSON objects.
public class Product implements Serializable {
#JsonProperty("Product name")
private String first;
#JsonProperty("Remaining stock")
private long second;
// implement methods for getters and setters
}
With Pair Class, you can't do that. I suggest you to create a DTO class like below :
public class CustomPair {
#JsonProperty("Product name")
private String first;
#JsonProperty("Remaining stock")
private String second;
// standard getters and setters
}
Then your controller will be like this :
#GetMapping("/remaining-stock")
public ResponseEntity<List<CustomPair>> showAllStock() throws EmptyDepositException{
List<CustomPair> allStock;
try {
allStock = depServ.showAllStock(); // depServ.showAllStock(); should return List<CustomPair>
}catch(EmptyDepositException ex) {
allStock = null;
}
return ResponseEntity.ok(allStock);
}

Enunciate not working with Generics

My resource is
#GET
#Path("/items")
public MyCollection<Items> getItems()throws Exception{
//Code to return MyCollection<items>
}
My Item class is
#XmlRootElement
public class Item{
private int id;
private String name;
//Have getters and Setters.
}
And My collection class is Generic as below.
public class MyCollection<T> extends MyBaseCollection{
private java.util.Collection<T> items;
private int count;
}
When i try to generate doc using enunciate. The sample Json has only the item and count and the fields of Item class is not getting reflected.
My sample Json generated is
{
"items" : [ {
}, {
}, {
}, {
}, {
}, {
}, {
}, {
} ],
"count" : ...,
}
How to get id,name inside the Item in the generated sample Json?
Thanks.
This is a limitation that i have run into as well, there is no way to specify #TypeHint on a nested object. To support documentation, consider creating a custom collection that defines "items" as a collection of specific class instead of generic.
If you have an idea of how you would want this to work (using the generic type) I suggest submitting enhancement request to Enunciate team.
I have a similar problem where I am returning a Map and I can't #TypeHint this.

JAX-RS & Jersey: Properly Nested JSON

I am fairly new to using JAX-RS and Jersey. The problem I am facing is that I cannot get a response in JSON the way I would like. I want a response that is something similar to the following:
{
"result": "success",
"car": {
"id": 42,
"name": "toyota",
"model": "camry"
}
}
So far I have a class named Car. It has 3 fields: id, name, and model with getters and setters. I get properly formatted JSON response of a single instance of a Car using the function whose return type is Car.
public Car getCarWithId(#PathParam("id") int id) {
return carService.getCarWithId(id);
}
This only gives me the following:
{
"id": 42,
"name": "toyota",
"model": "camry"
}
How can I get the response with the "result":"success". This will help me solve more complex problems in the future. Thanks for any help.
P.S. I thought about trying to return a hashmap but that gives me an error and seems like I would go into a deeper hole.
Assuming you're provider is Jackson, you could wrap it in a custom ResponseEntity object with a result field, along with a Map<String, Object> with the getter annotated with #JsonAnyGetter. For instance
public class ResponseEntity {
private String result;
private final Map<String, Object> otherFields = new HashMap<>();
public String getResult() {
return result;
}
#JsonAnyGetter
public Map<String, Object> getOtherFields() {
return otherFields;
}
public void addField(String field, Object value) {
otherFields.put(field, value);
}
}
What the #JsonAnyGetter does is make it so that the otherField is not serialized, but only the properties in the Map. You after you create the instance, and all entity.addField("car", carObject);, you will get your desired result.

Categories