I have a problem with returning an object in json format. This is my function to return a json object:
#GetMapping("/api/code/{n}")
#ResponseBody
public Code getCodeJson(#PathVariable int n) {
Code code = codeList.get(n - 1);
return code;
}
Objects of type Code are stored in a list, and I want to access them by the path variable n. The return looks like this:
{"id":1,"code":"{\"code\":\"hello world\"}","dateTime":"2021-10-05T16:49:31.911591"}
I don't know why this is happening, it should return a json that looks like this:
{"id":1,"code":"hello world","dateTime":"2021-10-05T16:49:31.911591"}
This is how I add code objects to the codeList
#PostMapping("/api/code/new")
#ResponseBody
public String addNewCode(#RequestBody String code) {
Code newCode = new Code(code);
codeList.add(newCode);
return "{\n" + "\"id\" : \"" + newCode.getId() + "\"\n}";
}
This is Code.java class
public class Code {
private static int currentId = 1;
private int id;
private String code;
private LocalDateTime dateTime;
public Code(String code) {
this.id = currentId;
this.code = code;
this.dateTime = LocalDateTime.now();
currentId++;
}
public int getId() {
return id;
}
public String getCode() {
return code;
}
public LocalDateTime getDateTime() {
return dateTime;
}
public void setCode(String code) {
this.code = code;
this.dateTime = LocalDateTime.now();
}
}
You can update the addNewCode to accept Code or new DTO CodeDto as the request body instead and use that instead. As of now you are getting the entire request body as string and assigning it to the code causing the current response.
#PostMapping("/api/code/new")
#ResponseBody
public String addNewCode(#RequestBody CodeDto code) {
Code newCode = new Code(code.getCode());
codeList.add(newCode);
return "{\n" + "\"id\" : \"" + newCode.getId() + "\"\n}";
}
As you are passing {"code":"hello world"} as request body the above code will automatically deserialize it properly into an instance of CodeDto object with code attribute set to "hello world".
public class CodeDto {
private String code;
public CodeDto(String code) {
this.code = code;
}
public CodeDto() {}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
}
Related
I am trying to use Jackson to parse sample json as demonstrated below. However, I the parsing doesn't work (fails without any exceptions - as I get an empty string for event.getAccountId(); What could I be doing wrong?
Thanks!
ObjectMapper om = new ObjectMapper();
String json = "{\"_procurementEvent\" : [{ \"accountId\" : \"3243234\",\"procurementType\" : \"view\"," +
"\"_procurementSubType\" : \"Standard Connector\",\"_quantity\" : \"4\", \"_pricePerMonth\" : \"100.00\"" +
",\"_annualPrice\" : \"1200.00\"}]}";
ProcurementEvent event = om.readValue(json, ProcurementEvent.class);
event.getAccountId(); // returns null
#JsonIgnoreProperties(ignoreUnknown = true)
private static class ProcurementEvent {
private String _accountId;
private String _procurementType;
private String _quantity;
private String _pricePerMonth;
private String _annualPrice;
#JsonProperty("accountId")
public String getAccountId() {
return _accountId;
}
public void setAccountId(String accountId) {
_accountId = accountId;
}
#JsonProperty("procurementType")
public String getProcurementType() {
return _procurementType;
}
public void setProcurementType(String procurementType) {
_procurementType = procurementType;
}
#JsonProperty("_quantity")
public String getQuantity() {
return _quantity;
}
public void setQuantity(String quantity) {
_quantity = quantity;
}
#JsonProperty("_pricePerMonth")
public String getPricePerMonth() {
return _pricePerMonth;
}
public void setPricePerMonth(String pricePerMonth) {
_pricePerMonth = pricePerMonth;
}
#JsonProperty("_annualPrice")
public String getAnnualPrice() {
return _annualPrice;
}
public void setAnnualPrice(String annualPrice) {
_annualPrice = annualPrice;
}
}
In the question, try the following approach:
class ProcurementEvents {
private List<ProcurementEvent> _procurementEvent; // + annotations like #JsonIgnoreProperties, getters/ setters, etc.
}
// json from your example
ProcurementEvents events = om.readValue(json, ProcurementEvents.class);
events.get(0).getAccountId();
Good morning guys!
I have a JSON strings that looks like:
{
"StatusCode":0,
"Message":null,
"ExecutionTime":0,
"ResponseData":[
{"Name":"name1","SiteId":"1234","Type":"Type1","X":"1234567","Y":"123456"},
{"Name":"Name2","SiteId":"2134","Type":"Type2","X":"1234567","Y":"1234567"},
{"Name":"Name3","SiteId":"3241","Type":"Type3","X":"1234567","Y":"1234567"},
{"Name":"Name4","SiteId":"4123","Type":"Type4","X":"123456","Y":"123456"}
]
}
I want to create an object where I can retrieve the Xand Y values.
I've been trying to use Jackson to serialize the JSON string, without success. I've created two extra classes for Jackson to use. One class for the top layer, StatusCode, Message, ExecutionTime and ResponseData which looks like
public class PL {
private Long statusCode;
private String executionTime;
private String message;
private ResponseData responseData;
public PL(){
}
public void setStatusCode(Long statusCode){
this.statusCode = statusCode;
}
public Long getStatusCode(){
return this.statusCode;
}
public void setExecutionTime(String executionTime){
this.executionTime = executionTime;
}
public String getExecutionTime(){
return this.executionTime;
}
public void setMessage(String message){
this.message = message;
}
public String getMessage(){
return this.message;
}
public void setResponseData(ResponseData responseData){
this.responseData = responseData;
}
public ResponseData getResponseData(){
return this.responseData;
}
}
Where ReponseData is returned as an object, and then I have another class for serializing ResponseData which looks like
public class ResponseData {
private String name;
private String siteId;
private String type;
private String x;
private String y;
public ResponseData(){
}
public void setName(String name){
this.name = name;
}
public String getName(){
return this.name;
}
public void setSiteId(String siteId){
this.siteId = siteId;
}
public String getSiteId(){
return this.siteId;
}
public void setType(String type){
this.type = type;
}
public String setType(){
return this.type;
}
public void setX(String x){
this.x = x;
}
public String getX(){
return this.x;
}
public void setY(String y){
this.y = y;
}
public String getY(){
return this.y;
}
}
I then create an ObjectMapper with
private final static ObjectMapper mapper = new ObjectMapper();
and try to so read the values with
ResponseData e = mapper.readValue(result.toString(), ResponseData.class);
and end up with the exception
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "StatusCode" (class MyClass.ResponseData), not marked as ignorable (5 known properties: "x", "y", "siteId", "name", "type"])
as if it can't parse the first entry, StatusMessage. Even if I remove the second class and only try to parse the first four entries where i return ResponseData as a String I still get the same exception.
To start with, in PL you should have a List<ResponseData> not a simple ResponseData attribute. As you can see, in the JSON, ResponseData is an array "ResponseData":[...] so it will be deserialized as a List. Each element of the list will be a ResponseData object as you defined it.
Then you have a case issue, you have upper cases in the JSON that you don't have in your class attributes. You can use the #JsonProperty (See API) annotation to overcome the problem, this way:
class PL {
#JsonProperty("StatusCode")
private Long statusCode;
#JsonProperty("ExecutionTime")
private String executionTime;
#JsonProperty("Message")
private String message;
#JsonProperty("ResponseData")
private List<ResponseData> responseDatas;
public PL(){
}
// getters/Setters
}
class ResponseData {
#JsonProperty("Name")
private String name;
#JsonProperty("SiteId")
private String siteId;
#JsonProperty("Type")
private String type;
#JsonProperty("X")
private String x;
#JsonProperty("Y")
private String y;
public ResponseData(){
}
// getters/Setters
}
Then read your JSON as a PL object, like this:
ObjectMapper mapper = new ObjectMapper();
PL pl = mapper.readValue(json, PL.class);
for(ResponseData rd : pl.getResponseDatas()) {
System.out.println(rd.getX());
System.out.println(rd.getY());
}
This outputs:
1234567
123456
1234567
1234567
1234567
1234567
123456
123456
It is fairly straightforward. Define your response structure using composition of classes. It is unfortunate to use capitalised fields in JSON, which out-of-the-box requires capitalised field names in the Java DTO. Still those can be easily mapped to conventional low-case names either by using the ACCEPT_CASE_INSENSITIVE_PROPERTIES modifier on the ObjectMapper or by annotating fields with corresponding names. I prefer a property on the ObjectMapper as it keeps the DTO independent of the serialisation code and this technique is used in the test below (the test is green):
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
public class TestDeserialization50386188 {
public static class Response {
public static class ResponseDataType {
public String name;
public String siteId;
public String type;
public long x;
public long y;
}
public int statusCode;
public String message;
public long executionTime;
public List<ResponseDataType> ResponseData = new ArrayList<>();
}
private static final String data = "{\"StatusCode\":0,\"Message\":null,\"ExecutionTime\":0,\"ResponseData\":[{\"Name\":\"name1\",\"SiteId\":\"1234\",\"Type\":\"Type1\",\"X\":\"1234567\",\"Y\":\"123456\"},{\"Name\":\"Name2\",\"SiteId\":\"2134\",\"Type\":\"Type2\",\"X\":\"1234567\",\"Y\":\"1234567\"},{\"Name\":\"Name3\",\"SiteId\":\"3241\",\"Type\":\"Type3\",\"X\":\"1234567\",\"Y\":\"1234567\"},{\"Name\":\"Name4\",\"SiteId\":\"4123\",\"Type\":\"Type4\",\"X\":\"123456\",\"Y\":\"123456\"}]}";
#Test
public void deserialize_response_withJackson_ok() throws IOException {
ObjectMapper mapper = new ObjectMapper()
.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);
Response response = mapper.readValue(data, Response.class);
assertEquals(4, response.ResponseData.size());
assertEquals(1234567, response.ResponseData.get(2).x);
assertEquals(1234567, response.ResponseData.get(2).y);
}
}
You fill find the project with the executable test on this dedicated GitHub repo.
The "Clean Code" book by Uncle Bob does not really recommend the overuse of getters and setters so common in Java for DTOs, which a Response class is. Still you can replace all public fields with getter/setter pairs if you like but the clarity will suffer with no obvious gain on quality.
Use List to receive arrays.
private Long statusCode;
private String executionTime;
private String message;
public List<ResponseDataType> ResponseData
and it will do everything automatically.
i need return different Lists on a WebService, currently i have more than 15 different types in an encapsulate file but is very hard manipulate many constructors:
public class ResponseMessage implements java.io.Serializable {
private Integer code;
private String message;
private List<Users> listUsers;
private List<Products> listProducts;
private List<Customers> listCustomers;
private List<Suppliers> listSuppliers;
private List<Reports> listReports;
...
private Users userById;
private Products productById;
private Customers customerById;
private Suppliers supplierById;
...
public ResponseMessage() {
}
//My idea is something like that, not work
public ResponseMessage(Integer code, String message, List<T> lstData) {
this.code = code;
this.message = message;
this.lstData = lstData;
}
public ResponseMessage(Integer code, String message, T uniqueData) {
this.code = code;
this.message = message;
this.uniqueData = uniqueData;
}
//Currently the constructor are this, work
public ResponseMessage(Integer code, String message, List<Users> listUsers) {
this.code = code;
this.message = message;
this.listUsers = listUsers;
}
public ResponseMessage(Integer code, String message, List<Users> listUsers, List<Customers> listCustomers) {
this.code = code;
this.message = message;
this.listUsers = listUsers;
this.listCustomers = listCustomers;
}
public ResponseMessage(Integer code, String message, List<Users> listUsers, List<Customers> listCustomers, List<Suppliers> listSuppliers) {
this.code = code;
this.message = message;
this.listUsers = listUsers;
this.listCustomers = listCustomers;
this.listSuppliers = listSuppliers;
}
...
//Same history with unique result, work
public ResponseMessage(Integer code, String message, Users userById) {
this.code = code;
this.message = message;
this.userById = userById;
}
public ResponseMessage(Integer code, String message, Users userById, Products productById) {
this.code = code;
this.message = message;
this.userById = userById;
this.productById = productById;
}
//Getters and Setters
}
When i like return the constructor on the WebService i have to do it this, for example (work):
public ResponseMessage readAllSuppliers() {
List<Suppliers> lstsuppliers = new ArrayList<Suppliers>();
lstsuppliers = supplierDAO.getAllSuppliers();
//ResponseMessage(code, message, user, customer, supplier list or unique supplier)
ResponseMessage rm = new ResponseMessage(123, "reading suppliers", null, null, lstsuppliers);
return rm;
}
But i think you can do it like this for anyone list:
public ResponseMessage readAllSuppliers() {
List<Suppliers> lstsuppliers = new ArrayList<Suppliers>();
lstsuppliers = supplierDAO.getAllSuppliers();
//ResponseMessage(code, message, list or object data)
ResponseMessage rm = new ResponseMessage(123, "reading suppliers", lstsuppliers);
return rm;
}
At the end, get the info data something like this on a WebService Client:
public void getSuppliers() {
WebServiceResponse wsr = new WebServiceResponse();
ResponseMessage rm = wsr.readAllSuppliers();
System.out.println("CODE: " + rm.getCode()); //CODE: 123
System.out.println("MESSAGE: " + rm.getMessage()); //MESSAGE: reading suppliers
for (Suppliers data : rm.getLstData()) {
System.out.println("SUPPLIER INFO: " + data.getFullName());
}
//SUPPLIER INFO: Name1 Surname1
//SUPPLIER INFO: Name2 Surname2
//SUPPLIER INFO: Name3 Surname3
}
I hope you can help me
You just need to add <T> after your ResponseMessage declaration to tell Java you want to use generics. Hope this helps. Note it expects to give back one type only per response. If you need to send more than one type, it might be a good idea to use a Map of Types to Lists instead of lstData as mentioned by #v1shnu.
public class ResponseMessage<T> implements Serializable {
private final List<T> lstData;
private final Integer code;
private final String message;
public ResponseMessage(Integer code, String message, List<T> lstData) {
this.code = code;
this.message = message;
this.lstData = lstData;
}
}
You could consider doing the same thing for your data access objects.
You can create an HashMap , before you send it to ResponseMessage like below :
Map<String,List<T>> listsMap = new HashMap<>();
listsMap.put("users",userDao.readAllUsers());
listsMap.put("customers",customerDao.readAllCustomers());
listsMap.put("suppliers",supplierDao.readAllSuppliers());
ResponseMessage rm = new ResponseMessage(123, "message", listsMap);
In your ResponseMessage class, you can add a constructor like below :
public ResponseMessage (Integer code, String message,Map listsMap){
this.code = code;
this.message = message;
this.listUsers = (List<Users>) listsMap.get("users");
this.listCustomers = (List<Customers>) listsMap.get("customers");
this.listSuppliers = (List<Suppliers>) listsMap.get("suppliers");
}
I am using Spring's RestTemplate to convert a JSON response from the RiotAPI into my BasicSummoner object. I believe the issue is with converting the JSON response into my object. After calling getForObject() all of the object's fields are null/empty. Any help is appreciated as this is my first Spring project and first time using Riot's API.
I have verified that the JSON resonse is correct and looks like this:
{
"riotschmick": {
"id": 585897,
"name": "RiotSchmick",
"profileIconId": 782,
"summonerLevel": 30,
"revisionDate": 1469155559000
}
}
My request looks like this:
public BasicSummoner requestBasicSummoner() {
RestTemplate template = new RestTemplate();
String mes = "https://na.api.pvp.net/api/lol/na/v1.4/summoner/by-name/RiotSchmick?api_key=<my-api-key>";
BasicSummoner summoner = template.getForObject(mes, BasicSummoner.class);
log.info(summoner.toString());
return summoner;
}
And the object BasicSummoner looks like this:
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
#JsonIgnoreProperties(ignoreUnknown = true)
public class BasicSummoner {
private long id;
private String name;
private int profileIconId;
private long revisionDate;
private long summonerLevel;
public BasicSummoner() {
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getProfileIconId() {
return profileIconId;
}
public void setProfileIconId(int profileIconId) {
this.profileIconId = profileIconId;
}
public long getRevisionDate() {
return revisionDate;
}
public void setRevisionDate(long revisionDate) {
this.revisionDate = revisionDate;
}
public long getSummonerLevel() {
return summonerLevel;
}
public void setSummonerLevel(long summonerLevel) {
this.summonerLevel = summonerLevel;
}
#Override
public String toString() {
return "id=" + id + ", name=" + name + " , summoner level=" + summonerLevel;
}
}
Your JSON is not a single Object, but an Object inside another Object.
This means that to use your code as it is now, you need to unwrap the inner Object, or change the structure to something else.
The response seems to fit a Map<String, BasicSummoner>
I am getting "Expected BEGIN_ARRAY but was STRING at line 1 column 1 path $" when I am retrieve the list from database.How to fix this issue where I am mistaken in my code..thanks in advance.
Json response I am getting is below,
[{
"eventId":1,
"ringeeUserId":2,
"text":"Reception1",
"place":"Erode",
"eventDate":"2015-10-03",
"startTime":"09:00 AM",
"endTime":"12:00 PM",
"isActive":0,
"isDelete":0,
"eventUserRelationBOs":[]
}]
EventBO class file is below,
package com.ringeeapp.service.businessobjects;
import java.io.Serializable;
import java.util.List;
public class EventBO implements Serializable {
private static final long serialVersionUID = 281625146097131515L;
private long eventId;
private long ringeeUserId;
private String text;
private String place;
private String eventDate;
private String startTime;
private String endTime;
private int isActive;
private int isDelete;
private List<EventUserRelationBO> eventUserRelationBOs;
public long getEventId() {
return eventId;
}
public void setEventId(long eventId) {
this.eventId = eventId;
}
public long getRingeeUserId() {
return ringeeUserId;
}
public void setRingeeUserId(long ringeeUserId) {
this.ringeeUserId = ringeeUserId;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public String getPlace() {
return place;
}
public void setPlace(String place) {
this.place = place;
}
public String getEventDate() {
return eventDate;
}
public void setEventDate(String eventDate) {
this.eventDate = eventDate;
}
public String getStartTime() {
return startTime;
}
public void setStartTime(String startTime) {
this.startTime = startTime;
}
public String getEndTime() {
return endTime;
}
public void setEndTime(String endTime) {
this.endTime = endTime;
}
public int getIsDelete() {
return isDelete;
}
public void setIsDelete(int isDelete) {
this.isDelete = isDelete;
}
public int getIsActive() {
return isActive;
}
public void setIsActive(int isActive) {
this.isActive = isActive;
}
public List<EventUserRelationBO> getEventUserRelationBOs() {
return eventUserRelationBOs;
}
public void setEventUserRelationBOs(List<EventUserRelationBO> eventUserRelationBOs) {
this.eventUserRelationBOs = eventUserRelationBOs;
}
}
The below code that process json data
public #ResponseBody String getAllInvites(#RequestParam("userBO") String userBo) {
List<EventBO> eventBOs = new ArrayList<EventBO>();
UserBO userBO = gson.fromJson(userBo, UserBO.class);
try {
eventBOs = manageEventServiceImpl.getAllInvites(userBO);
log.info("getting all events for user " + userBO.getRingeeUserId());
} catch (UserServiceException serExp) {
log.error("Error while getting event for userId id" + userBO.getRingeeUserId(), serExp);
}
return gson.toJson(eventBOs);
}
You have an array of EventBO, but trying to read as object. If you unmarshal like this:
EventBO bo = gson.fromJson(JSON, EventBO.class);
than you will receive error "java.lang.IllegalStateException: Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 2 path $"
But if you change code to:
EventBO[] list = gson.fromJson(JSON, EventBO[].class);
you successfully unmarshal this json
P.S. Tested with Gson 2.4
Kotlin
Late answer and i don't think many face this issue but, i was dealing with a dumb server, that returned the data however it wanted, strange huh?
yes,
//response 1
{
"genre":"drama,thriller"
}
//response 2
{
"genre":null
}
//response 3
{
"genre":["drama", "action", "blah", "blah"]
}
You can think how unfair this is.
i so changed my genre's data type to Any?
like
data class Model(...
....
val genre:Any?
...
)
now to use it, i do something like this
movieInfo.mInfo.genre?.let {
if (it is String) {
tv_genre.text = it
} else if (it is List<*>) {
val builder = StringBuilder()
it.forEach {
if (it is String) {
builder.append(it)
}
}
tv_genre.text = builder.toString()
}
}