In the first case when it returns success true, everything works, the problem when it gets success boolean is false, then the error:
How Retrofit Response Array or Object either.
Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 26 path $ .data
Can it be done with one reponse class?
Json response:
{
"success": true,
"data": {
"message": "User created",
}
}
Json response:
{
"success": false,
"data": [
{
"code": "existing_user_login",
"message": "User Exist !"
}
]
}
Code:
public class Response {
public Boolean success;
public Data data;
public Boolean isSuccess() { return success; }
public Data getData() {
return data;
}
public class Data {
public String code;
public String message;
public String getMessage() { return message; }
public String getCode() { return code; }
}
}
you could use Object data first, and judge it before use it, like following:
public class Response {
public Boolean success;
public Object data;
public Boolean isSuccess() { return success; }
public Object getData() {
if(data instanceof Data){
//do something
}else if (data instanceof List) {
//do something
}else {
//do something
}
return data;
}
public class Data {
public String code;
public String message;
public String getMessage() { return message; }
public String getCode() { return code; }
}
}
Using ObjectMapper you can do it by configuring deserialization features as below:
// import com.fasterxml.jackson.databind.ObjectMapper;
ObjectMapper objMapper = new ObjectMapper();
// this is to accept single element as array
objMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
// this is to handle if any property is missing
objMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
Response resp = objMapper.readValue(jsonString, Response.class);
Related
I develop simple todo-list application with spring boot. And I wanna send common response dto from all request. The data filed in ReponseMessage is Generic because it can be various type.
public class ResponseMessage<T> {
private int statusCode = HttpStatus.OK.value();
private String message = "";
protected T data; // this is generic. it can be many response dto.
public static ResponseMessage<String> ok() {
return new ResponseMessage<>("");
}
public static <T> ResponseMessage<T> ok(T data) {
return new ResponseMessage<>(data);
}
public static ResponseMessage<Object> noContent() {
return new ResponseMessage<>(HttpStatus.NO_CONTENT.value(), HttpStatus.NO_CONTENT.getReasonPhrase(), "");
}
protected ResponseMessage(T data) {
this.data = data;
}
private ResponseMessage(int statusCode, String message, T data) {
this.statusCode = statusCode;
this.message = message;
this.data = data;
}
protected ResponseMessage() {}
public int getStatusCode() {
return statusCode;
}
public String getMessage() {
return message;
}
public T getData() {
return data;
}
}
Now i need to make a feature that find a recend todo.
If todo exists, send ResponseMessage.
{
"statusCode": 200,
"message": "",
"data": [
{
"id": 1,
"title": "test",
"status": "TODO"
}
]
}
If todo doesn't exist, send ResponseMessage.
{
"statusCode": 200,
"message": "",
"data": []
}
Now my controller function return like this.
public ResponseMessage<?> findRecent(#AuthenticationPrincipal LoginUser user) {
Optional<TodoResponse> todo = todoService.findRecent(user.getId());
return todo.isPresent() ? ResponseMessage.ok(todo.get()) : ResponseMessage.noContent();
}
I know that the question mark '?' is not good. so what is better way to return A or B in this case? plz help me.
If you set noContent().data to null, you can safely assign it any type:
public static <T> ResponseMessage<T> noContent() {
return new ResponseMessage<>(HttpStatus.NO_CONTENT.value(), HttpStatus.NO_CONTENT.getReasonPhrase(), null);
}
Also note that calling Optional.isPresent() and then get() is something of an antipattern. In your case, you can simplify it to this:
return todoService.findRecent(user.getId())
.map(ResponseMessage::ok)
.orElseGet(ResponseMessage::noContent);
I've got a JSON like this:
{
"result": [
{
"reservation_id": 101,
"euro_fee": 11.00,
"hotel_id": 1
},
{
"reservation_id": 102,
"euro_fee": 12.00,
"hotel_id": 2
},
{
"reservation_id": 103,
"euro_fee": 13.00,
"hotel_id": 3
}
],
"meta": {
"ruid": "0001="
}
}
and I'm trying to use Jackson (with Spring Boot) for parse and bind it. Here is my POJO's:
Response.java
public class Response {
private Result result;
private Meta meta;
public Response() {
}
public Result getResult() {
return result;
}
public void setResult(Result result) {
this.result = result;
}
public Meta getMeta() {
return meta;
}
public void setMeta(Meta meta) {
this.meta = meta;
}
}
Meta.java
public class Meta {
private String ruid;
public Meta() {
}
public String getRuid() {
return ruid;
}
public void setRuid(String ruid) {
this.ruid = ruid;
}
}
Result.java
public class Result {
private Booking[] results;
public Result() {
}
public Booking[] getResults() {
return results;
}
public void setResult(Booking[] results) {
this.results = results;
}
}
Booking.java
public class Booking {
private long reservation_id;
private long hotel_id;
private Double euro_fee;
public Booking() {
}
public long getReservation_id() {
return reservation_id;
}
public void setReservation_id(long reservation_id) {
this.reservation_id = reservation_id;
}
public long getHotel_id() {
return hotel_id;
}
public void setHotel_id(long hotel_id) {
this.hotel_id = hotel_id;
}
public Double getEuro_fee() {
return euro_fee;
}
public void setEuro_fee(Double euro_fee) {
this.euro_fee = euro_fee;
}
}
I can get ruid from meta using:
// getting JSON from REST
String response = restTemplate.postForObject(resourceURL, httpEntity, String.class);
// use jackson
ObjectMapper mapper = new ObjectMapper();
Response theResponse = mapper.readValue(response, Response.class);
System.out.println("getRuid: " + theResponse.getMeta().getRuid());
but I can't get objects or single item from nested array. When I'm trying to get array of items I'm getting error:
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of out of START_ARRAY token at [Source: (String)...
I know this one should be easy, but I'm using Jackson for the first time and the problem might be somewhere in the deep.
Change your Response class.
Try with this:
public class Response {
private List<Booking> result;
private Meta meta;
//getter setter
}
I am trying to parse a JSON file in Java. But the format of the JSON file is complex (at least for me) and hence I have run into a roadblock.
You may read the file-
Reddit JSON file
Within the "reddit" file, the 'key' I am interested in is "body" which is nested deep within the JSON array within the file.
I am using Jackson for parsing and deserialization of the JSON to a Java object and my code is as follows-
class Reddit
{
private String kind;
private String data;
public String getData()
{
return this.data;
}
public void setData(String data)
{
this.data = data;
}
public String getKind()
{
return this.data;
}
public void setKind(String data)
{
this.data = data;
}
}
public class Attempting_Read_JSON
{
public static void main(String[] args)
{
// ObjectMapper can parse JSON into Java Objects, can do vice-versa and more-
ObjectMapper OM = new ObjectMapper();
try
{
out.println("\nInside try block.\n");
File json_file = new File("src/main/resources/data/reddit/redditdump.json");
Reddit[] R = OM.readValue(json_file, Reddit[].class);
out.println("\nRead from JSON file to 'Reddit class object'\n");
//out.println("\nObtained JSON- " + R.getData());
}
catch (Exception E)
{
E.printStackTrace();
}
out.println("\n\nProgram terminated successfully!\n");
}
}
But on executing the program, it keeps giving me errors.
Any suggestions on how I might get the value associated with the key- 'body'?
Thanks!
In your JSON file "data": is an object and you are trying to assign to String in Reddit class through private String data;.
That's why you are getting this error.
You either need to create a class say Data with members contest_mode, banned_by.... or use JSON Object to parse it.
Also why are you setting data here use kind
public String getKind()
{
return this.data;
}
public void setKind(String data)
{
this.data = data;
}
EDIT:
For simplicity I will give you small example:
JSON file:
[
{
"kind": "Listing",
"data": {
"contest_mode": false
}
},
{
"kind": "Listing",
"data": {
"contest_mode": false
}
}
]
Your code:
public class Attempting_Read_JSON {
public static void main(String[] args) {
ObjectMapper OM = new ObjectMapper();
try {
out.println("\nInside try block.\n");
File json_file = new File(
"src/main/resources/data/reddit/redditdump.json");
Reddit[] R = OM.readValue(json_file, Reddit[].class);
out.println("\nRead from JSON file to 'Reddit class object'\n" + R[0].toString());
// out.println("\nObtained JSON- " + R.getData());
} catch (Exception E) {
E.printStackTrace();
}
out.println("\n\nProgram terminated successfully!\n");
}
}
class Reddit {
private String kind;
#Override
public String toString() {
return "Reddit [kind=" + kind + ", data=" + data.toString() + "]";
}
private Data data;
public Data getData() {
return this.data;
}
public void setData(Data data) {
this.data = data;
}
public String getKind() {
return this.kind;
}
public void setKind(String kind) {
this.kind = kind;
}
}
class Data
{
boolean contest_mode;
public boolean isContest_mode() {
return contest_mode;
}
public void setContest_mode(boolean contest_mode) {
this.contest_mode = contest_mode;
}
#Override
public String toString() {
return "[contest_mode=" + contest_mode + "]";
}
}
Hi i cant work out why I am getting null values for my response. Im using Retrofit library on Android.
raw json
{
"images": [
{
"image": {
"name": "nike adver",
"url": "http:\/\/wallpaperbarcelona.com\/wp-content\/uploads\/2013\/07\/neymar-nike-advert.jpg",
"type": "photo"
}
}]
}
// interface
public interface PromoImagesAPI {
#GET("/FriendsCMS/images/?type=photo&format=json")
void promoImages(Callback<ImagesObject> callback);
}
request function
private void requestNewsData(String uri) {
RestAdapter api = new RestAdapter.Builder().setEndpoint(ENDPOINT).build();
PromoImagesAPI restapi = api.create(PromoImagesAPI.class);
restapi.promoImages(new Callback<Images>() {
#Override
public void success(Images imageObjects, Response response) {
System.out.println("webservice " +response.getUrl());
for (int i = 0; i < imageObjects.images.size(); i++) {
System.out.println("webservice " +imageObjects.getImages().get(i).getUrl());
}
}
#Override
public void failure(RetrofitError error) {
System.out.println("webservice " + error);
}
});
}
Pojo
public class ImagesObject {
public List<Images> images;
public class Images {
public String name;
public String url;
public String type;
public String getName() {
return name;
}
public String getUrl() {
return url;
}
public String getType() {
return type;
}
public void setName(String name) {
this.name = name;
}
public void setUrl(String url) {
this.url = url;
}
public void setType(String type) {
this.type = type;
}
}
}
The thing is the amount of elements in the for loop is correct, i have tested that, the values are all null. Have i missed something, any help would be gratefully appreciated . thanks
use http://www.jsonschema2pojo.org/ to create your java object model and do the following to call
public interface PromoImagesAPI {
#GET("/FriendsCMS/images/?type=photo&format=json")
void promoImages(Callback<Images> callback);
Yes, I answer by agreeing. I also missed the #Expose annotation. This can happen specially when using third party tools to convert from json to kotlin or java classes. I also used Gson to convert from json when doing unit testing and everything was passing gracefully until I ran the app and everything came back with null values
I'm working with Mule Studio in this, and I actually was wondering the possibility to add the drop down list with a "custom" string name. The case applies for MIME Types (ContentTypes) names. (i.e: text/css, text/javascript, and so)
Made an structure like this:
public enum HttpContentType {
TEXT_PLAIN
{
public String toString() {
return "text/plain";
}
},
TEXT_CSS
{
public String toString() {
return "text/css";
}
},
TEXT_JS
{
public String toString() {
return "text/javascript";
}
},
TEXT_XML
{
public String toString() {
return "text/xml";
}
},
};
To then be used like this as a Configurable attribute:
/**
* Connection Content Types
*/
#Configurable
#Placement(order=1,group="Configuration",tab="HTTP Configuration")
private HttpContentType contentType;
But of course that when reading that from the Mule Studio it will be a Drop Down list with names such as: TEXT_CSS, TEXT_XML, TEXT_JAVASCRIPT ( http://puu.sh/3vLbd.png ) and so instead of text/css, text/xml, text/javascript.
How can i achieve this result?
A far better way to achieve this would be to provide a field in your enum, and provide an appropriate constructor:
public enum HttpContentType {
TEXT_PLAIN("text/plain"),
TEXT_CSS("text/css");
private String value;
HttpContentType(String value) {
this.value = value;
}
public String toString() {
return this.value;
}
public static HttpContentType getByValue(String value){
for (final HttpContentType element : EnumSet.allOf(HttpContentType.class)) {
if (element.toString().equals(value)) {
return element;
}
}
return null;
}
}
One option is to provide a different method to get the MIME string.
public interface IMimeTypeProvider {
public String toMimeType();
}
public enum HttpContentType implements IMimeTypeProvider {
TEXT_PLAIN
{
public String toMimeType() {
return "text/plain";
}
},
...
public String toMimeType() {
return "text/plain";
}
}
You might also consider adding a toDisplayString() method. The enumerator name is not necessarily a good display name, even in a single locale.
public enum HttpContentType {
TEXT_PLAIN("text/plain"), TEXT_CSS("text/css");
private String code;
HttpContentType(String code) {
this.code = code;
}
public String toString() {
return code;
}
public static HttpContentType getEnum(String code) {
if (code.equals("text/plain")) {
return TEXT_PLAIN;
} else if (code.equals("text/css")) {
return TEXT_CSS;
} else {
return null;
}
}
}