I'm a .Net guy. I tried to call API and getting data from.
Serialized JSON object using java.. but I'm getting this error.
Expected BEGIN_ARRAY but was BEGIN_OBJECT
In .NET it's really easy to do. but in java, I haven't idea how to do that.
this my model class
public class Dictionary {
#SerializedName("metadata")
#Expose
private Metadata metadata;
#SerializedName("results")
#Expose
private List<Result> results = null;
public Metadata getMetadata() {
return metadata;
}
public void setMetadata(Metadata metadata) {
this.metadata = metadata;
}
public List<Result> getResults() {
return results;
}
public void setResults(List<Result> results) {
this.results = results;
}
}
And here the my api call
public class Oxford {
public String URL_OXFORD = "https://od-api.oxforddictionaries.com/api/v1/inflections/en/swimming";
public static List<Dictionary> httpGet(String url, String function) {
List<Dictionary> dataFromService = new ArrayList<Dictionary>();
try (CloseableHttpClient httpClient = HttpClientBuilder.create().build()) {
HttpGet request = new HttpGet(url+function);
request.addHeader("content-type", "application/json");
request.addHeader("app_id", "566566");
request.addHeader("app_key", "somekey");
HttpResponse result = httpClient.execute(request);
String json = EntityUtils.toString(result.getEntity(), "UTF-8");
com.google.gson.Gson gson = new com.google.gson.Gson();
Dictionary[] response = gson.fromJson(json, Dictionary[].class);
System.out.println(response.length);
for(Dictionary file : response)
{
dataFromService.add(file);
System.out.println(file.toString());
}
} catch (IOException ex) {
}
return dataFromService;
}
Since the documentation returns one object (starting with {, not a [) you probably need a
Dictionary response
instead of
Dictionary[] response
It is returning one dictionary object, not an array. Checkout the documentation at https://developer.oxforddictionaries.com/documentation
Related
I'm new to the Java 11 HttpClient and would like to give it a try. I have a simple GET request that return JSON and I would like to map the JSON response to a Java class called Questionnaire.
I understand that I can turn the response out of box into a String or an input stream like this
HttpRequest request = HttpRequest.newBuilder(new URI(String.format("%s%s", this.baseURI, "/state")))
.header(ACCEPT, APPLICATION_JSON)
.PUT(noBody()).build();
HttpResponse<String> response = this.client.send(request, HttpResponse.BodyHandlers.ofString());
How can I write something that converts the JSON string to my Questionnaire class like this?
HttpResponse<Questionnaire> response = this.client.send(request, HttpResponse.BodyHandlers./* what can I do here? */);
I use Jackson to transform JSON into Java class instances. Is there Jackson support for the new Java standard HttpClient yet?
UPDATE 1
I was not precise enough, sorry about that. I am looking for a blocking get example. I was aware of http://openjdk.java.net/groups/net/httpclient/recipes.html#jsonGet
Solution for Java 11 HttpClient::sendAsync only
Based on this link you can do something like this :
public static void main(String[] args) throws IOException, URISyntaxException, ExecutionException, InterruptedException {
UncheckedObjectMapper uncheckedObjectMapper = new UncheckedObjectMapper();
HttpRequest request = HttpRequest.newBuilder(new URI("https://jsonplaceholder.typicode.com/todos/1"))
.header("Accept", "application/json")
.build();
Model model = HttpClient.newHttpClient()
.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::body)
.thenApply(uncheckedObjectMapper::readValue)
.get();
System.out.println(model);
}
class UncheckedObjectMapper extends com.fasterxml.jackson.databind.ObjectMapper {
/**
* Parses the given JSON string into a Map.
*/
Model readValue(String content) {
try {
return this.readValue(content, new TypeReference<Model>() {
});
} catch (IOException ioe) {
throw new CompletionException(ioe);
}
}
}
class Model {
private String userId;
private String id;
private String title;
private boolean completed;
//getters setters constructors toString
}
I used some dummy endpoint which provides sample JSON input and sample model class to map the response directly to Model class using Jackson.
Solution for Java 11 HttpClient::send and HttpClient::sendAsync
I found a way by defining custom HttpResponse.BodyHandler :
public class JsonBodyHandler<W> implements HttpResponse.BodyHandler<W> {
private Class<W> wClass;
public JsonBodyHandler(Class<W> wClass) {
this.wClass = wClass;
}
#Override
public HttpResponse.BodySubscriber<W> apply(HttpResponse.ResponseInfo responseInfo) {
return asJSON(wClass);
}
public static <T> HttpResponse.BodySubscriber<T> asJSON(Class<T> targetType) {
HttpResponse.BodySubscriber<String> upstream = HttpResponse.BodySubscribers.ofString(StandardCharsets.UTF_8);
return HttpResponse.BodySubscribers.mapping(
upstream,
(String body) -> {
try {
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.readValue(body, targetType);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
});
}
}
Then I call it :
public static void main(String[] args) throws URISyntaxException, IOException, InterruptedException {
HttpRequest request = HttpRequest.newBuilder(new URI("https://jsonplaceholder.typicode.com/todos/1"))
.header("Accept", "application/json")
.build();
Model model = HttpClient.newHttpClient()
.send(request, new JsonBodyHandler<>(Model.class))
.body();
System.out.println(model);
}
The response is :
Model{userId='1', id='1', title='delectus aut autem', completed=false}
The JavaDoc of HttpResponse.BodySubscribers::mapping was particulary useful to solve this. It can be further improved to use HttpResponse.BodySubscribers::ofInputStream instead of HttpResponse.BodySubscribers.ofString(StandardCharsets.UTF_8) to define the BodySubscriber for the JsonBodyHandler.
Simplifying #michalk solution for Java 11 HttpClient::send
HttpService Class Example:
public class HttpService {
private final HttpClient httpClient= HttpClient.newBuilder().version(HttpClient.Version.HTTP_2).build();
public HttpService() {}
public <T> T sendGetRequest(String url, Class<T> responseType) throws IOException, InterruptedException {
HttpRequest request = HttpRequest.newBuilder().GET().uri(URI.create(url)).header("Accept", "application/json").build();
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
return new ObjectMapper().readValue(response.body(), responseType);
}
public <T> List<T> sendGetListRequest(String url, Class<T> responseType) throws IOException, InterruptedException {
HttpRequest request = HttpRequest.newBuilder().GET().uri(URI.create(url)).header("Accept", "application/json").build();
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.readValue(response.body(), objectMapper.getTypeFactory().constructCollectionType(List.class, responseType));
}}
Model Class Example:
public class Model {
private String id;
public Model() {}
public String getId() { return this.id; }
public void setId(String id) { this.id = id; }
#Override
public String toString() { return "Model{" + "id='" + id + '\'' + '}'; }}
Sending HTTP GET request:
public class Main {
public static void main(String[] args) {
try {
HttpService httpService = new HttpService();
Model model = httpService.sendGetRequest("http://localhost:8080/api/v1/models/1", Model.class);
System.out.println("Single Object:" + model);
System.out.print('\n');
List<Model> models = httpService.sendGetListRequest("http://localhost:8080/api/v1/models", Model.class);
for(Model m: models) { System.out.println("Object:" + m); }
}
catch (IOException | InterruptedException e) {
System.err.println("Failed to send GET request: " + e.getMessage());
}
}}
Response:
Single Object: Model{id='1'}
Object: Model{id='1'}
Object: Model{id='2'}
Object: Model{id='3'}
Required Maven Dependency (pom.xml):
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.3</version>
</dependency>
If you're OK with including a dependency, check out Methanol (disclaimer: I'm the library's author). The library has special BodyHandler implementations for object mapping. You can add JSON support by installing the jackson adapter.
var request = MutableRequest.GET("https://example.com")
.header("Accept", "application/json");
var modelResponse = client.send(request, MoreBodyHandlers.ofObject(Model.class));
// Use TypeRef<T> for complex types
var modelListResponse = client.send(request, MoreBodyHandlers.ofObject(new TypeRef<List<Model>>() {}));
i have React JS application where i send post request to server with form submission using axios library.
client request:
sendData(data,price) {
axios.post('http://localhost:8080/SampleJavaAPP/UserServer', {
item: data,//these value
price:price//these value
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
}
i am not sure how to get these values into server i am doing in server for getting value like this
String name = request.getParameter("item");
String price = request.getParameter("price");
System.out.println("Result "+name + price);
But it gives null values in server. how to receive these values parameters in server?
As Axios is sending Json data you will not be able to read its direct.
There are 2 possible solutions:
Either send data as form-data.
Read & Parse JSON at servlet:
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
StringBuffer jb = new StringBuffer();
String line = null;
try {
BufferedReader reader = request.getReader();
while ((line = reader.readLine()) != null) {
jb.append(line);
}
} catch (Exception e) { /*report an error*/ }
try {
JSONObject jsonObject = HTTP.toJSONObject(jb.toString());
String price = jsonObject.get("price"); // will return price value.
} catch (JSONException e) {
throw new IOException("Error parsing JSON request string");
}
}
Request Body is not retrieved by request.getParameter(). You need to retrieve it by request.getReader().
String body = IOUtils.toString(request.getReader());
It is suggested to use Apache Commons IO to get Content first. As your request is in JSON format. You can use Jackson to convert the String into Map.
Map<String, String> map = mapper.readValue(body, new TypeReference<Map<String, String>>(){});
System.out.println(map.get("item"));
System.out.println(map.get("price"));
request.getParameter()is referring to URL parameters ->myurl?someparameter=1
By doing request.getParameter("item"), your URL would need to look like http://localhost:8080/SampleJavaAPP/UserServer?item=myitem
What you are actually doing here
sendData(data,price) {
axios.post('http://localhost:8080/SampleJavaAPP/UserServer', {
item: data,//these value
price:price//these value
}
is adding the objects to the request body, which is IMHO correct. There for you won't find any parameters item or price on your server side request object.
What you need to do, is to parse the requests body. With request.getInputStream() you can get the Inputstream. I suggest you use an object mapper which makes that really easy. See Intro to the Jackson ObjectMapper
In your servlet, you do something like this:
ObjectMapper objectMapper = new ObjectMapper();
MyItem myItem = objectMapper.readValue(request.getInputStream(), MyItem.class);
public class MyItem{
String price;
String item;
public void setItem(String item) {
this.item = item;
}
public void setPrice(String price) {
this.price = price;
}
public String getItem() {
return item;
}
public String getPrice() {
return price;
}
}
I have a typical Retrofit API request:
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(URL)
.build();
ApiEndpointInterface api = restAdapter.create(ApiEndpointInterface.class);
api.getToken('1', new Callback<DefaultResponse>() {
#Override
public void success(DefaultResponse json, Response response) {
//
}
#Override
public void failure(RetrofitError response) {
//
}
});
And the returned JSON is:
{"success":true,"data":{"token_id":"pPt9AKl0Cg","token_key":"8ax224sFrJZZkStAQuER"}}
How can I parse this JSON? It seems wrong/tedious to create a new model class for each different response across my app. Is there a better way to do it?
you should write your model class like below
public class MyResponseModel {//write setters and getters.
private boolean success;
private DataModel data;
public static class DataModel {
private String token_id;
private String token_key;
}
}
now in your getToken() method should look like this
getToken('1', Callback<MyResponseModel> response);
retrofit will parse the response and convert it to the class above.
Try this code,
JsonElement je = new JsonParser().parse(s);
JsonObject asJsonObject = je.getAsJsonObject();
JsonElement get = asJsonObject.get("data");
System.out.println(s + "\n" + get);
JsonObject asJsonObject1 = get.getAsJsonObject();
JsonElement get1 = asJsonObject1.get("token_id");
System.out.println(get1);
JsonElement get2 = asJsonObject1.get("token_key");
System.out.println(get2);
So I already succeed with one Json to get it all worked. Now I have another class where I want to get only one attribute. I have now a moviedatabase class (which work with JSON and gets all the information) and now I want to add a Trailer which is from Youtube API. so basically I need it to be added into the same JSON to make it easier for me in the future to get it into a HTML. the only problem is I cant get it work. I get a syntax error JSON when using this method.
EDIT CODE 1.1:
Youtube attribute:
public class FilmAttribut {
private String title = "";
private String release = "";
private int vote = 0;
private String overview = "";
private String poster = "";
private String trailer = "";
// getters + setters stripped
}
Youtube class:
public class Youtube {
FilmAttribut movie = new FilmAttribut();
public void search(String trailer, FilmAttribut in) {
HttpResponse<JsonNode> response;
try {
response = Unirest.get("https://www.googleapis.com/youtube/v3/search?key=[app key here]&part=snippet")
.queryString("q", trailer + " trailer")
.asJson();
JsonNode json = response.getBody();
JSONObject envelope = json.getObject();
JSONArray items = envelope.getJSONArray("items");
in.setTrailer("https://youtu.be/" + items.getJSONObject(0).getJSONObject("id").getString("videoId")); //Gives me error here
}
catch (JSONException e) {
e.printStackTrace();
} catch (UnirestException e) {
e.printStackTrace();
}
return null;
}
}
and main method
public class WebService {
public static void main(String[] args) {
setPort(1337);
Gson gson = new Gson();
Youtube yt = new Youtube();
MovieDataBase mdb = new MovieDataBase();
get("/search/:movie", (req, res) -> {
String titel = req.params(":movie");
FilmAttribut film = mdb.searchMovie(titel);
yt.search(titel, film);
String json = gson.toJson(film);
return json;
});
So I think the problem is that you can't have two gson.toJson(film) + gson.toJson(trailer); Because it makes the JSON twice, where one time is for the film (aka. movie) and then a new json is created with trailer which make the syntax error.
So my real question is, is it possible to have another class like I have now youtube. to send the information to a attribute class where I have all my attributes and then run it in main-method so that I can get all the JSON in one JSON.
If I did understand well what you are asking, yes you can, but I would do something like that instead:
public void search(String trailer, FileAttribut in) {
// fetch the trailer from youtube (NB: you should use getters/setters, not public fields)
in.setTrailer("https://youtu.be/" + items.getJSONObject(0).getJSONObject("id").getString("videoId"));
}
and:
FilmAttribut film = mdb.searchMovie(titel);
yt.search(titel, film); // pass "film" to "fill" its trailer
return gson.toJson(film);
OR
public String search(String trailer) {
// fetch the trailer from youtube
return "https://youtu.be/" + items.getJSONObject(0).getJSONObject("id").getString("videoId");
}
and:
FilmAttribut film = mdb.searchMovie(titel);
film.setTrailer(yt.search(titel));
return gson.toJson(film);
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How can I stop HTTP from escaping quotes?
I am creating a JSONObject and sending JSON string to a server in a POST request body.
public String toJson() {
JSONObject filter = new JSONObject();
try {
filter.put("gender", gender.getCode());
filter.put("feature_id", productCategory);
} catch (JSONException e) {
e.printStackTrace();
}
JSONObject filterObject = new JSONObject();
try {
filterObject.put("filter", filter);
} catch (JSONException e) {
e.printStackTrace();
}
return filterObject.toString();
}
So I'm creating a request:
private IJsonExecutorInterface requestExecutorForRelativePathAndParams(String path, WebParams params) throws UnsupportedEncodingException {
HttpPost postRequest = new HttpPost(rootUrl + path);
if(params != null) {
postRequest.setHeader("content-type", params.getContentType());
postRequest.setEntity(params.getFormEntity());
}
// Blah blah
return executor;
}
public IJsonExecutorInterface getProducts(ProductFilter filter, int offset, int limit) throws UnsupportedEncodingException {
WebParams webParams = new WebParams();
webParams.addPair("filter", filter.toJson());
webParams.addPair("offset", String.format("%d", offset));
webParams.addPair("limit", String.format("%d", limit));
return requestExecutorForRelativePathAndParams("products", webParams);
}
// WebParams class
public class WebParams {
private ArrayList<NameValuePair> params;
private String contentType = "application/x-www-form-urlencoded";
public WebParams() {
params = new ArrayList<NameValuePair>();
}
public void addPair(String name, String value) {
params.add(new BasicNameValuePair(name, value));
}
public String getContentType() {
return contentType;
}
public HttpEntity getFormEntity() throws UnsupportedEncodingException {
return new UrlEncodedFormEntity(params);
}
}
I see it in debugger: it's ok.
But on my server I getting something like this:
Array
(
[filter] => {\"gender\":\"w\",\"feature_id\":\"41_7459\"}
[offset] => 0
[limit] => 18
)
The quotes ARE escaped.
I don't want to replace something on the server. replace("\\\"", "\"") in Java doesn't affect on the string.
Looks like your using a UrlEncodedFormEntity, which, according to the docs is 'An entity composed of a list of url-encoded pairs' ([http://developer.android.com/reference/org/apache/http/client/entity/UrlEncodedFormEntity.html]). I've never used this, but it doesn't sound like its what you want, as you are sending data in the post body, not through URL parameters.
I've used the StringEntity class before to send json data via post, although it only encodes a string, not name/value pairs, so you've got to do a bit more work to put the string in a format you want to deal with on your server:
public class WebParams {
private ArrayList<NameValuePair> params;
private String contentType = "application/x-www-form-urlencoded";
public WebParams() {
params = new ArrayList<NameValuePair>();
}
public void addPair(String name, String value) {
params.add(new BasicNameValuePair(name, value));
}
public String getContentType() {
return contentType;
}
public HttpEntity getFormEntity() throws UnsupportedEncodingException {
//TODO: Build a string in what ever format you want.
// This will include the gender & feature_id fields as well as the json
StringBuilder b = new StringBuilder();
for(NameValuePair nvp : params) {
builder.append(nvp.getName()).append('=').append(nvp.getValue()).append(',');
}
//Now that we have a string to send to the server, get your entity!
StringEntity entity = new StringEntity(b.toString());
entity.setContentEncoding(new BasicHeader(HTTP.CONTENT_TYPE, "application/json"));
return entity;
}
}
Is there a problem to use simple quotes instead of double quotes? Because I think it would solve your problem.