Java consuming restful api with httpclient. Having trouble with Jackson json MismatchedInputException - java

This is my first time using jackson/consuming apis/httpclient. I'm getting this error com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize value of type java.util.ArrayList<WallHaven> from Object value (token JsonToken.START_OBJECT) . The api I'm trying to consume is https://wallhaven.cc/help/api
try {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.GET()
.uri(URI.create("https://wallhaven.cc/api/v1/w/pkgkkp"))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
ObjectMapper mapper = new ObjectMapper();
List<WallHaven> posts = mapper.readValue(response.body(), new TypeReference<List<WallHaven>>() {
});
posts.forEach(System.out::println);
} catch (Exception e) {
e.printStackTrace();
}
The api json format is https://pastebin.com/tbSaVJ1T
Here's my WallHaven class
public class WallHaven {
public Data data;
public WallHaven(Data data) {
this.data = data;
}
public WallHaven() {
}
#Override
public String toString() {
return "WallHaven{" +
"data=" + data.getPath() +
'}';
}
}
Data contains all the other classes/variables

This is happening because you're trying to deserialize a Json Object into a List in java. The error message explains it by saying that the starting character (JsonToken.START_OBJECT) is the start of a json object not a json array, so you can't deserialize it directly into a List, but should deserialize it into an object.
Try changing:
List<WallHaven> posts = mapper.readValue(response.body(), new TypeReference<List<WallHaven>>())
into
WallHaven post = mapper.readValue(response.body(), new TypeReference<WallHaven>())

Related

Send JSON in request OkHttp

Friends!
I have a simple HTTP request:
void postRequest(String postUrl,String phone, String message) throws IOException {
OkHttpClient client = new OkHttpClient();
//RequestBody body = RequestBody.create(JSON, postBody);
RequestBody body = new FormBody.Builder()
.add("phone", phone)
.add("message", message)
.build();
Request request = new Request.Builder()
.url(postUrl)
.post(body)
.build();
//System.out.println(request);
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
call.cancel();
}
#Override
public void onResponse(Call call, Response response) throws IOException {
Log.d("TAG",response.body().string());
}
});
}
How to properly implement sending a JSON object instead of simple parameters?
My attempts were unsuccessful, so I really need a hint.
The server that will accept JSON is running on AKKA-HTTP.
How do I send a request to this server correctly?
final case class Message(phone: String, message: String, service: String)
implicit val item = jsonFormat3(Message)
val queue: Queue[Message] = Queue()
val addMessage = post {
path("add_message"){
parameters("phone".as[String], "message".as[String], "service".as[String]){
(phone, message, service) => {
queue.enqueue(Message(phone, message, service))
complete("ok")
}
}
}
}
The easiest way to map and serialize your object in JSON format is to use the ObjectMapper class of jackson-databind library.
I personally use it to implement integration tests of RestControllers and it works very well. Here is the utility class I realized, you can take it and use it for your purposes:
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public final class JsonUtils {
public static String json(Object obj) throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.writeValueAsString(obj);
}
}
What you need to have is a POJO class which implements Serializable, and then pass the instance of your class to the json method and it will return the JSON format.
You can definitely use it for Android projects. I found many examples where you can add the dependency, but it depends whether you use Gradle or Maven.
Try that out!!!
How do you like this option?
I tried to implement it, but the send fails.
I'm missing an important detail. But I don't understand what it is.
// create your json here
JSONObject jsonObject = new JSONObject();
try {
jsonObject.put("KEY1", "VALUE1");
jsonObject.put("KEY2", "VALUE2");
} catch (JSONException e) {
e.printStackTrace();
}
OkHttpClient client = new OkHttpClient();
MediaType JSON = MediaType.parse("application/json; charset=utf-8");
// put your json here
RequestBody body = RequestBody.create(JSON, jsonObject.toString());
Request request = new Request.Builder()
.url("https://YOUR_URL/")
.post(body)
.build();
Response response = null;
try {
response = client.newCall(request).execute();
String resStr = response.body().string();
} catch (IOException e) {
e.printStackTrace();
}

Handle inconsistent API response with Retrofit

I'm facing an issue in new project. I'm connecting to API that could response two different data types in one response based on some server logic. I'm using Retrofit on Android and I was wondering if there's some "easy" way to handle that cases before retrofit object parse, eg. some kind of parser/serializer that would check what type has specific JSON field? I dunno.
Here are possible responses:
error response:
{
"ReturnCode": "error",
"ReturnCodeNumber": 444,
"ReturnMessage": "Invalid Request",
"ReturnData": ""
}
data response:
{
"ReturnCode": "ok",
"ReturnCodeNumber": 0,
"ReturnMessage": "success",
"ReturnData": [
{
}
]
}
Retrofit API request:
#FormUrlEncoded
#POST("url")
Observable<ApiResponse<List<Data>>> requestData()
API response class has exposed fields of above response and parameterized T for returnData.
So is it possible to somehow wrap it in some serializer class?
You will have to write custom deserializer or register a type adapter as explained in this -
https://sites.google.com/site/gson/gson-user-guide#TOC-Custom-Serialization-and-Deserialization
Try to do by using TypeAdapterFactory. Sample of that class as shown below.
public class ItemTypeAdapterFactory implements TypeAdapterFactory {
#Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);
final TypeAdapter<JsonElement> elementAdapter = gson.getAdapter(JsonElement.class);
return new TypeAdapter<T>() {
public void write(JsonWriter out, T value) throws IOException {
delegate.write(out, value);
}
public T read(JsonReader in) throws IOException {
JsonElement jsonElement = elementAdapter.read(in);
if (jsonElement.isJsonObject()) {
JsonObject jsonObject = jsonElement.getAsJsonObject();
if (jsonObject.has("data") && jsonObject.get("data").isJsonObject()) {
jsonElement = jsonObject.get("data");
}
}
return delegate.fromJsonTree(jsonElement);
}
}.nullSafe();
}
}
And this Gson into RestAdapter
final Gson gson = new GsonBuilder().registerTypeAdapterFactory(new ItemTypeAdapterFactory()).create();
final Client client = new OkClient(new OkHttpClient());
final RestAdapter restAdapter = new RestAdapter.Builder().setClient(client).setLogLevel(RestAdapter.LogLevel.FULL).setConverter(new GsonConverter(gson)).setEndpoint(context.getString(R.string.base_url)).build();

Adding a new element to an array in the json object java

I am writing automation script to validate json responses of REST APIs and i am using faster xml to serialize and convert java object to json format.
I have a user case where I have to get the json response and add a new array element to an existing array and post it back.
The json response after GET looks like this :
{
"name":"test",
"id":"1234",
"nodes":[
{
"nodeId":"node1"
},
{
"nodeId":"node2"
}
]
}
To this json response, I need to add a third entry for nodes array
{ "nodeId": "node3" } and then post this.
Can someone please help me understand how to add a new array element to an existing array?
You can try:
//Your JSON response will be in this format
String response = "{ \"name\":\"test\", \"id\":\"1234\", \"nodes\":[ { \"nodeId\":\"node1\" }, { \"nodeId\":\"node2\" } ] }";
try {
JSONObject jsonResponse = new JSONObject(response);
JSONArray nodesArray = jsonResponse.getJSONArray("nodes");
JSONObject newEntry = new JSONObject();
newEntry.put("nodeId","node3");
nodesArray.put(newEntry);
jsonResponse.put("nodes",nodesArray);
} catch (JSONException e) {
e.printStackTrace();
}
Now you can post your jsonResponse.toString() as required.
I would rather go for cleaner approach, create Object with below structure -
public class Response{
private String name;
private int id;
private List<Node> nodes;
<Getter & Setter>
}
public class Node{
private String nodeId;
}
Serialize the json -
Response response = objectMapper.readValue(responseJson,
Response.class);
Add the new incoming node object to response -
response.getNodes().add(New Node("{new node Value}"));
Deserialize before post -
objectMapper.writeValueAsString(response);

Retrofit 2 - get error-object JSON (few POJO for same request)

I have simple request like
/*LOGIN*/
#FormUrlEncoded
#POST("v1/user/login") //your login function in your api
Call<LoginResponce> login(#Field("identity") String identity,
#Field("password") String password);
Which returns either LoginResponceobject if http code 200
{"token":"itwbwKay7iUIOgT-GqnYeS_IXdjJi","user_id":17}
Or Error Json, describes exact error, if something went wrong
{"status":4,"description":"user provided token expired"}
How can I handle error status in response?
I tried this, but it doesn't see JSON in raw text (doens't work). And doesn't seems to be nice solution.
mCallLoginResponse.enqueue(new Callback<LoginResponce>() {
#Override
public void onResponse(Response<LoginResponce> response, Retrofit retrofit) {
if (response.isSuccess()) {
registerWithToken(response.body().getToken());
} else { //some error in responce
Gson gson = new GsonBuilder().create();
ApiError mApiError = gson.fromJson(response.raw().body().toString(),
ApiError.class); //Exception here - no JSON in String
//todo error handling
}
}
To get access to the response body when you have an error code, use errorBody() instead of body(). Also, there is a string method on ResponseBody that you should use instead of toString.
Gson gson = new GsonBuilder().create();
try {
ApiError mApiError = gson.fromJson(response.errorBody().string(),ApiError.class);
} catch (IOException e) {
// handle failure to read error
}

Retrofit POST contains null body

I'm setting up an api client with retrofit, and so far GETs are working fine, but I'm trying to create a new object with a POST, and instead of the object being sent as json, the request body just contains the string "null":
---> HTTP POST http://myapiurl
Content-Type: application/json; charset=UTF-8
Content-Length: 4
null
---> END HTTP (4-byte body)
Here is the method I'm trying to call:
#POST("/Monsters/")
Response new_monster(#Body Monster mon);
And here is how I'm calling it:
#Test
public void testNew_monster() throws Exception {
//create a new monster object, and pass it to the function,
//then query and verify it's in the results?
Monster newmon = new Monster() {
{
name = "deleteme";
description = "created by junit test testNew_monster";
image_url = "http://i.imgur.com/";
created_by = "";
encoded_key = "";
}
};
Response r = client.new_monster(newmon);
assertEquals(201, r.getStatus());
//sleep a couple seconds here?
List<Monster> monsterList = client.monsters();
assertTrue(monsterList.contains(newmon));
}
I am guessing something is going wrong when the object is serialized to json with GSON, but I'm unable to see anything helpful with the debugger during the serialization process...
I'm using GSON version 2.3.1
EDIT: Here's how I'm building the RestAdapter and the client:
static MonSpottingApi GetClient(boolean dbg)
{
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(API_URL)
.build();
if (dbg) restAdapter.setLogLevel(RestAdapter.LogLevel.FULL);
MonSpottingApi client = restAdapter.create(MonSpottingApi.class);
return client;
}
In the test case class:
MonSightingClient.MonSpottingApi client;
#Before
public void setUp() throws Exception {
client = MonSightingClient.GetClient(true);
}
I suspected that the root cause was Gson, so I started making very simple tests and trying to get objects to correctly serialize using toJson(). I think I found a bug in Gson, where it fails if an object is initialized with double-brace syntax:
Using the example class found here
public class GitHubTest {
//fails
#Test
public void testGson1() throws Exception {
GitHubClient.Contributor contrib = new GitHubClient.Contributor() {
{
login = "someguy";
contributions = 99;
}
};
Gson gson = new Gson();
String json = gson.toJson(contrib);
System.out.println("contents of json string: " + json);
assertNotEquals(json, "null");
}
//passes
#Test
public void testGson2() throws Exception {
GitHubClient.Contributor contrib = new GitHubClient.Contributor();
contrib.contributions = 99;
contrib.login = "someguy";
Gson gson = new Gson();
String json = gson.toJson(contrib);
System.out.println("contents of json string: " + json);
assertNotEquals(json, "null");
}
}
Is this a bug in Gson? Or is there some weird subtle Java reason that this happens? (Java is not my strongest language).
I had the same issue, and it looks like a Gson is not happy with the double-brace initialization of the object that is to be converted.
Replacing the double-brace initialization...
AuthenticationRequest req = new AuthenticationRequest() {{
setName("myName");
setPassword("myPass");
}}
with...
AuthenticationRequest req = new AuthenticationRequest();
req.setUserId("myName");
req.setPassword("myPass");
...did the trick.
You have to pass your interface class to create() method of Restadapter. Let's say your interface class is INewService (where you declared new_monster), then GetClient should look like:
public static INewService GetClient(boolean dbg){
RestAdapter restAdapter = new RestAdapter.Builder().
.setEndpoint(API_URL).
.setClient(new OkClient(new OkHttpClient()))
.build();
if (dbg) restAdapter.setLogLevel(RestAdapter.LogLevel.FULL);
return restAdapter.create(INewService.class);
}

Categories

Resources