How to create a costum JSON response in Spring Java? - java

Currently, I am trying to return a costum created response from my REST API. My goal is to create the following response.
{
"status": "Success",
"message": "More details here",
"data": {
"records": [
{
"record": "Lorem ipsum",
"code": 0,
"errors": null
},
{
"record": "Lorem ipsum",
"code": 1,
"errors": [
"First error",
"Second error"
]
}
]
}
}
My API endpoint looks like the following.
#PostMapping(value="/my-url",
consumes=MediaType.APPLICATION_JSON_VALUE,
produces=MediaType.APPLICATION_JSON_VALUE)
public Response check_records(#RequestBody(required=true) Records records) {
// Do Stuff
}
The 'Response' class:
public class Response {
private String status;
private String message;
private Map<String, List<RecordResponse>> data;
// Default getter / setter
}
The 'RecordResponse' class:
public class RecordResponse {
private String record;
private Integer code;
private String[] errors;
The response itself is returning anything but JSON. Neither way it is returning an error or returns the object reference in the json:
{
"status": "Success",
"message": "More details here",
"data": {com.example.restservice.data#2071af5d}
}

You can use generics to have a decent typecontroll for your response wrapper
public class Response<T> {
private String status;
private String message;
private T data;
//all args contructor
}
and
public class Records{
List<RecordResponse> records;
}
public class RecordResponse {
private String record;
private Integer code;
private String[] errors;
public Response<Records> check_records(#RequestBody(required=true) Records records) {
RecordResponse recordResponses=//build responses
Records records=//build records
return new Response<Records>(status,message,records);
}
This should produce response close if not exact as you have requested

Related

rest api - PUT method does not consume the data that GET method fetches

I obtain data with GET method and try to fed them to PUT method.
And I get a bad request error.
But when I edit the JSON as below then everithing works fine.
So why does the first JSON not work?
Controller class:
#RestController
#RequestMapping("/api")
public class FileController {
private final FileService fileService;
private final DocService docService;
private final DraftFileToPresentationConverter draftFileToPresentationConverter;
#Autowired
private DocFileRelationRepository docFileRelationRepository;
#Autowired
public FileController(FileService fileService,
DocService docService,
DraftFileToPresentationConverter draftFileToPresentationConverter) {
this.fileService = fileService;
this.docService = docService;
this.draftFileToPresentationConverter = draftFileToPresentationConverter;
}
#GetMapping("/docs/files/{id}")
public ResponseEntity<FilePresentation> getDraftFile(#PathVariable Long id) {
DraftFile file = fileService.getFile(id);
FilePresentation filePresentation = draftFileToPresentationConverter.convert(file);
return new ResponseEntity<>(filePresentation, HttpStatus.OK);
}
#PutMapping("/docs/files/{id}")
public ResponseEntity<FilePresentation> updateDraftFile(#RequestBody FilePresentation filePresentation) {
fileService.update(draftFileToPresentationConverter.convert(filePresentation));
return new ResponseEntity<>(filePresentation, HttpStatus.OK);
}
DTO:
#Data
public class FilePresentation {
private Long id;
private States state;
private String name;
private String user;
private Date dateUpload;
private Long lenght;
private IdRef document;
private IdRef judgeDoc;
public String getSize()
{
Double result = Double.valueOf(lenght/1024.0/1024.0);
if(result<1)
{
result = Double.valueOf(lenght/1024.0);
if(result<1)
{
return (lenght + " байт");
}
return (result.intValue() + " Кбайт");
}
return (result.intValue() + " Мбайт");
}
}
Troublesome class:
#Data
public class IdRef {
public IdRef(Long id) {
this.id = id;
}
private Long id;
}
JSON that I get with GET method and try to fed to PUT method (and get 400 Bad Request):
{
"id": 21,
"state": "DRAFT",
"name": "DNS-list.tiff",
"user": "JSmith",
"dateUpload": null,
"lenght": 28,
"document": {
"id": 141
},
"judgeDoc": null,
"size": "28 байт"
}
JSON that DOES work
{
"id": 21,
"state": "DRAFT",
"name": "DNS-list.tiff",
"user": "JSmith",
"dateUpload": null,
"lenght": 28,
"document": 141,
"judgeDoc": null,
"size": "28 байт"
}
Try to update RequestBody with #PathVariable
The constructor in IdRef was the reason.
I removed the constructor and it works fine now, my controller consumes the first JSON without errors.

How can i make a model for this Json file for Retrofit?

The Json file is:
{
"data": [
{
"name": "key",
"error": "key is not valid"
},
{
"name": "package_name",
"error": "package name is not valid"
}
],
"success": false,
"message": "information is not valid"
}
I've got a BaseModel which has "success","message", "data" and all of my responds are extended from this Class.
But "data is different for each response from the server.
I've made this so far:
public class BaseModel{
private Object data;
private boolean success;
private String message;
}
which data for this case of error will cast to an array of DataError:
public class DataError{
private String name;
private String error;
}
And i get an error which tells me :
java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be cast to com.example.mapp.Model.DataError
List<DataError> dataError = (List<DataError>)response.body().getData();
textView.append("Error:"+ dataError.get(0).getError());
You need to have a List<DataError> when parsing, Although it can also parse as a Map. Create data in BaseModel Generic this way you can reuse it other classes as well. naming it to BaseListModel cause it return a list.
public class BaseListModel<T>{
private List<T> data;
private boolean success;
private String message;
}
Now you can make the API call return BaseListModel<DataError> .
For parsing Object type responses you can create other base class .
public class BaseModel<T>{
private T data;
private boolean success;
private String message;
}

NullPointerException when loading JSON data with RetroFit using two queries

I'm developing an app that pulls data in JSON format from an API when a user searches a name, and can pick between searching for an artist or album. I'm using RetroFit to call the JSON data and want to use two queries in my interface.
Code to call JSON data:
public void ArtistSearch() {
String searchTerm = searchTxt.getText().toString().trim();
String searchCat = searchPick.getSelectedItem().toString().trim();
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
Retrofit.Builder builder = new Retrofit.Builder().baseUrl(api_url).addConverterFactory
(GsonConverterFactory.create());
final Retrofit retrofit = builder.client(httpClient.build()).build();
ArtistClient client = retrofit.create(ArtistClient.class);
Call<JSONResponse> call = client.getArtistList(searchTerm, searchCat);
call.enqueue(new Callback<JSONResponse>() {
#Override
public void onResponse(Call<JSONResponse> call, Response<JSONResponse> response) {
List<ArtistList> artists = response.body().getArtists();
Log.d(TAG, "Artists found");
Log.d(TAG, "Number of artists:" + artists.size());
artistAdapter = new ArtistAdapter(DiscogSearchPage.this, artists);
discogList.setAdapter(artistAdapter);
}
#Override
public void onFailure(Call<JSONResponse> call, Throwable t) {
Log.e(TAG, "Fail: " + t.getMessage());
}
});
}
Interface:
public interface ArtistClient {
#GET("/database/search")
Call<JSONResponse> getArtistList (#Query("q") String searchTerm, #Query("type") String searchCategory);
}
Currently when I try to run this I get the error
java.lang.NullPointerException: Attempt to invoke virtual method 'java.util.List ...Responses.JSONResponse.getArtists()' on a null object reference.
However if I comment out the second query in the interface and remove searchCat from the code the data loads fine. I have no idea what is going on.
Any help is appreciated.
Edit: For those asking for model classes and JSON response.
JSON response class:
public class JSONResponse {
#SerializedName("results")
#Expose
private List<ArtistList> artists = null;
// Getters and setters
ArtistList class:
public class ArtistList {
#SerializedName("thumb")
#Expose
private String thumb;
#SerializedName("title")
#Expose
private String title;
#SerializedName("uri")
#Expose
private String uri;
#SerializedName("cover_image")
#Expose
private String coverImage;
#SerializedName("resource_url")
#Expose
private String resourceUrl;
#SerializedName("type")
#Expose
private String type;
#SerializedName("id")
#Expose
private int id;
// Getters and setters
JSON response, with search "Drake" and type "artist":
{
"pagination": {
"per_page": 50,
"items": 22893,
"page": 1,
"urls": {
"last": "https://api.discogs.com/database/search?%3Ftype=artist&q=drake&secret=AEmHdfwlGwPqUYQpTBVrarEtzjKsykih&key=wMWTrOWaTkfHDUQVXFSG&per_page=50&page=458",
"next": "https://api.discogs.com/database/search?%3Ftype=artist&q=drake&secret=AEmHdfwlGwPqUYQpTBVrarEtzjKsykih&key=wMWTrOWaTkfHDUQVXFSG&per_page=50&page=2"
},
"pages": 458
},
"results": [
{
"thumb": "https://img.discogs.com/Voe5_n4NEBvrIW2AczQyGb389WM=/150x150/smart/filters:strip_icc():format(jpeg):mode_rgb():quality(40)/discogs-images/A-151199-1520497976-7068.jpeg.jpg",
"title": "Drake",
"uri": "/artist/151199-Drake",
"cover_image": "https://img.discogs.com/nFMZ1bVcXA3bLHxR_0LVSrRB7iM=/456x615/smart/filters:strip_icc():format(jpeg):mode_rgb():quality(90)/discogs-images/A-151199-1520497976-7068.jpeg.jpg",
"resource_url": "https://api.discogs.com/artists/151199",
"type": "artist",
"id": 151199
},
// results objects continue

GSON parsing of nested array

Im having difficulties understanding if GSON can handle this kind of json by default or do I need to implement deserializers for every sub element.
json input
{
"services":[
{
"id": 2,
"name": "Buy"
},
{
"id": 3,
"name": "Sell"
}
]
"status": {
"code": 0,
"message": ""
}
}
The best case result on my part is to have the following class contain all the data
java [ POJO ]
public class Services {
public List<ServiceItem> services;
public Status status;
public class ServiceItem {
public int id;
public String name;
}
public class Status {
public int code;
public String message;
}
}
Is it possible to let GSON the class and the json and just let it work? Or do I need to create deserializers for each sub class?
Correct your json input as follow (you forgot a comma before status field)
{
"services":[
{
"id": 2,
"name": "Buy"
},
{
"id": 3,
"name": "Sell"
}
],
"status": {
"code": 0,
"message": ""
}
}
Then let's consider your classes as follow
public class Services {
public List<ServiceItem> services;
public Status status;
// getters and setters
#Override
public String toString() {
return "["+services.toString()+status.toString()+"]";
}
public class ServiceItem {
public int id;
public String name;
// getters and setters
#Override
public String toString() {
return "("+id+","+name+")";
}
}
public class Status {
public int code;
public String message;
// getters and setters
#Override
public String toString() {
return ",("+code+","+message+")";
}
}
}
If the input is a file jsonInput.json then
Gson gson = new Gson();
Services data = gson.fromJson(new BufferedReader(new FileReader(
"jsonInput.json")), new TypeToken<Services>() {
}.getType());
System.out.println(data);
If the input is a json String jsonInput then
Gson gson = new Gson();
Services data = gson.fromJson(jsonInput, Services.class);
System.out.println(data);
Output:
[[(2,Buy), (3,Sell)],(0,)]

What should be my class structure to parse JSON in GSON

I have the following JSON data:
{
"response": {},
"errorMessage": {
"error": [
{
"errorId": 260003,
"domain": "ads",
"subdomain": "asd",
"severity": "asd",
"category": "asd",
"message": "asdsa asd ad",
"errorName": "UnAuthorized"
}
]
}
}
Currently I have the following class structure:
public class JSONCollection
private Response response;
private ErrorMessage error;
public class Response
private String collectionId;
private String url;
public class ErrorMessage
private List<ErrorValues> error;
public class ErrorValues
private String errorId;
private String domain;
private String subdomain;
private String severity;
private String category;
private String message;
private String errorName;
I have setters/get set for all private variables
But when I do a JSONCollection cJson = gson.fromJson(JSONValue,JSONCollection.class); I get cJson as a null.
How to get it right?
I used this tool shown by #JigarJoshi to generate my schema.
The only difference I found is I had to change the class name from ErrorValues to Error

Categories