How to get array of objects from JSON response using GSON - java

I am writing a REST client using RestTemplate and GSON.
Below is sample of my JSON response
{
"value": [
{
"properties": {
"vmId": "f7f953fb-d853-4373-b564-",
"hardwareProfile": {
"vmSize": "Standard_D2"
},
},
"name": "A",
"Id": ""
},
{
"properties": {
"vmId": "f7f953fb-d853-4373-b564-",
"hardwareProfile": {
"vmSize": "Standard_D2"
},
},
"name": "B",
"Id": ""
},
{
"properties": {
"vmId": "f7f953fb-d853-4373-b564-",
"hardwareProfile": {
"vmSize": "Standard_D2"
},
},
"name": "C",
"Id": ""
}
]
}
What I want to is that I want to get only the values for the property --> "name"
So I created a simple POJO that has only name as the member field.
public class VMNames {
public String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
and I am trying to use the GSON like this to get a array of this POJO. Here, the response is my JSON response object.
Gson gson = new Gson();
VMNames[] vmNamesArray = gson.fromJson(response.getBody(), VMNames[].class);
System.out.println(vmNamesArray.length);
But when I do this, I get an error i.e. as below:
java.lang.IllegalStateException: Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 path $
Please note that I don't want to create a POJO that has exactly same structure as my JSON object because I want to get only one attribute out of my JSON object. I am hoping that I won't have to really create a POJO with the same structure as my JSON response because, in reality, it's a huge response and I don't control it, so it can also change tomorrow.

Can you try this:
public class VMNames {
#SerializedName("name")
public String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Type collectionType = new TypeToken<Collection<VMNames>>(){}.getType();
Collection<VMNames> vmNamesArray = gson.fromJson(response.getBody(), collectionType);
System.out.println(vmNamesArray.length);
or try:
VMNames[] vmNamesArray = gson.fromJson(response.getBody(), VMNames[].class);

So I could get this done. Posting the answer to this so that someone can be benefited tomorrow :)
First thing, i stopped using GSON and started using JSON.
And below is the code that helped.
RestTemplate restTemplate = new RestTemplate();
response = (ResponseEntity<String>) restTemplate.exchange(url, HttpMethod.GET, entity, String.class);
JSONObject jsonObj = new JSONObject(response.getBody().toString());
JSONArray c = jsonObj.getJSONArray("value");
for (int i = 0; i < c.length(); i++) {
JSONObject obj = c.getJSONObject(i);
String VMName = obj.getString("name");
VMNames vmnames = new VMNames();
vmnames.setName(VMName);
vmNames.add(vmnames);
}
return vmNames;
And i get a list of all the value against the attribute name in form of a json array.

Related

Json string to Object mapping with dynamic values

I am consuming Thirdparty jsonString, I am trying to parse the json but sometimes JSON object "RadarReports" is an list and sometimes it object.
{"RadarReports": {
"executionTime": "135",
"RadarReport": {
"abc": "1116591",
"name": "abc",
"id": "2019050311582056119",
"ownerId": "xyz"
},
"size" :"1"
}}
=================
{"RadarReports": {
"executionTime": "113",
"RadarReport": [
{
"abc": "1116591",
"name": "abc",
"id": "2019050311582056119",
"ownerId": "xyz"
},
{
"abc": "1116591",
"name": "abc",
"id": "2019050311582056119",
"ownerId": "xyz"
},
]
"size" : "2"
}}
I tried below to parse but failing when single object came into picture, need to accept both single and list of objects.
#Data
public class Radarreports {
private int size;
private ArrayList<RadarreportSet> RadarReportSet;
private ArrayList<RadarReport> RadarReport;
}
#Data
public
class ReportsResponse {
Radarreports RadarReports;
}
URL url = new URL(queryUrl);
BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream()));
Gson gson = new GsonBuilder().create();
ReportsResponse radarReports = gson.fromJson(br, ReportsResponse.class);
You could solve this with a custom TypeAdapterFactory which creates an adapter which first peeks at the type of the JSON data and then adds special handling where the JSON object is not wrapped in an JSON array:
// Only intended for usage with #JsonAdapter
class SingleObjectOrListAdapterFactory implements TypeAdapterFactory {
#Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
// Note: Cannot use getDelegateAdapter due to https://github.com/google/gson/issues/1028
TypeAdapter<T> listAdapterDelegate = gson.getAdapter(type);
TypeAdapter<JsonObject> jsonObjectAdapter = gson.getAdapter(JsonObject.class);
return new TypeAdapter<T>() {
#Override
public void write(JsonWriter out, T value) throws IOException {
listAdapterDelegate.write(out, value);
}
#Override
public T read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.BEGIN_OBJECT) {
// Wrap JSON object in a new JSON array before parsing it
JsonObject jsonObject = jsonObjectAdapter.read(in);
JsonArray jsonArray = new JsonArray();
jsonArray.add(jsonObject);
return listAdapterDelegate.fromJsonTree(jsonArray);
} else {
return listAdapterDelegate.read(in);
}
}
};
}
}
The factory can then be specified for the affected field with #JsonAdapter:
#JsonAdapter(SingleObjectOrListAdapterFactory.class)
private ArrayList<RadarReport> RadarReport;

Spring RestTemplate parse JSON object with variable keyname

I have a REST API call that returns the following JSON object. I need to parse this with Spring's RestTemplate. The problem is that the first key ISBN:0132856204 is variable (the numbers change depending on the book). How would I go about doing this?
{
"ISBN:0132856204": {
"publishers": [
{
"name": "Pearson"
}
],
"pagination": "xxiv, 862p",
"identifiers": {
"isbn_13": [
"978-0-13-285620-1"
],
"openlibrary": [
"OL25617855M"
]
},
"weight": "1340 grams",
"title": "Computer networking",
"url": "https://openlibrary.org/books/OL25617855M/Computer_networking",
"number_of_pages": 862,
"cover": {
"small": "https://covers.openlibrary.org/b/id/7290810-S.jpg",
"large": "https://covers.openlibrary.org/b/id/7290810-L.jpg",
"medium": "https://covers.openlibrary.org/b/id/7290810-M.jpg"
},
"publish_date": "2013",
"key": "/books/OL25617855M",
"authors": [
{
"url": "https://openlibrary.org/authors/OL31244A/James_F._Kurose",
"name": "James F. Kurose"
},
{
"url": "https://openlibrary.org/authors/OL658909A/Keith_W._Ross",
"name": "Keith W. Ross"
}
],
"subtitle": "A Top-Down Approach"
}
}
In here "ISBN:0132856204" is a value and also a key for your business.
To get ISBN first, what about wrapping json content with 1 more closure?
{
"yourAwesomePlaceHolderKey" :
{
"ISBN:0132856204": {
......
}
}
}
First get the ISBN key as a value, then your ISBN value can be used as a key to get related content.
First goal will be extracting -String1,Object1- pair where String1 is "yourAwesomePlaceholderKey" and second goal will be again extracting -String2,Object2- from Object1 where String2 is your ISBN key.
This is the way I solved it, using JsonPath for getting the book out of the JSON object and Jackson for mapping it to a Book object:
RestTemplate restTemplate = new RestTemplate();
String isbn = "0132856204";
String endpoint = "https://openlibrary.org/api/books?jscmd=data&format=json&bibkeys=ISBN:{isbn}";
//Get JSON as String
String jsonString = restTemplate.getForObject(endpoint, String.class, isbn);
//Configure JsonPath to use Jackson for mapping
Configuration.setDefaults(new Configuration.Defaults() {
private final JsonProvider jsonProvider = new JacksonJsonProvider();
private final MappingProvider mappingProvider = new JacksonMappingProvider();
#Override
public JsonProvider jsonProvider() {
return jsonProvider;
}
#Override
public MappingProvider mappingProvider() {
return mappingProvider;
}
#Override
public Set<Option> options() {
return EnumSet.noneOf(Option.class);
}
});
//Parse the JSON as a book
Book book = JsonPath.parse(jsonString).read("$.ISBN:" + isbn, Book.class);
You can use JsonProperty to solve
#JsonProperty("ISBN:0132856204")

Deserialize JSON to POJO using Retrofit and Gson

I am working with an API that responds like the following for a single user resource:
{
"data": {
"id": 11,
"first_name": "First",
"last_name": "Last",
"books": {
"data": [
{
"id": 13,
"name": "Halo"
}
]
},
"games": {
"data": [
{
"id": 1,
"name": "Halo"
}
]
}
}
}
or like the following for multiple user resources:
{
"data": [
{
"id": 11,
"first_name": "First",
"last_name": "Last",
"books": {
"data": [
{
"id": 13,
"name": "Halo"
}
]
},
"games": {
"data": [
{
"id": 1,
"name": "Halo"
}
]
}
},
],
"meta": {
"pagination": {
"total": 11,
"count": 10,
"per_page": 10,
"current_page": 1,
"total_pages": 2,
"links": {
"next": "http://api.###.com/users?page=2"
}
}
}
}
Key things to notice are:
all resources are nested under a data key, single as an object or multiple as an array of objects. This includes nested resources such as books and games in the example above.
I need to be able retrieve the values of the meta key for my pagination routines
User model
public class User extends BaseModel {
public Integer id;
public String firstName;
public String lastName;
public List<Book> books; // These will not receive the deserialized
public List<Game> games; // JSON due to the parent data key
}
Custom JSON deserializer
public class ItemTypeAdapterFactory implements TypeAdapterFactory {
public <T> TypeAdapter<T> create(Gson gson, final 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 the data key exists and is an object or array, unwrap it and return its contents
if (jsonObject.has("data") && (jsonObject.get("data").isJsonObject() || jsonObject.get("data").isJsonArray())) {
jsonElement = jsonObject.get("data");
}
}
return delegate.fromJsonTree(jsonElement);
}
}.nullSafe();
}
}
This is all working fine but I can't figure out how to access the meta key for pagination.
Ideally I would get Gson to deserialize the response to the following POJO:
public class ApiResponse {
public Object data;
public Meta meta
}
and I could just cast the response field to the correct type in the response callback like the following:
Map<String, String> params = new HashMap<String, String>();
params.put("include", "books,games");
ApiClient.getClient().authenticatedUser(params, new ApiClientCallback<ApiResponse>() {
#Override
public void failure(RestError restError) {
Log.d("TAG", restError.message);
}
#Override
public void success(ApiResponse response, Response rawResponse) {
User user = (User) response.data; // Cast data field to User type
Log.d("TAG", user.firstName);
Log.d("TAG", "Total pages" + response.meta.pagination.total.toString()); // Still have access to meta key data
}
});
However the data field of the ApiResponse object is null.
My Java is very rusty and I have no idea if this is even possible nor do I understand how to go about it correctly, any help would be much appreciated.
I needed the same thing and have managed to get it working by adding an if statement to your custom serializer:
…
// If the meta key exists, consider the element to be root and don't unwrap it
if (!jsonObject.has("meta")) {
// If the data key exists and is an object or array, unwrap it and return its contents
if (jsonObject.has("data") && (jsonObject.get("data").isJsonObject() || jsonObject.get("data").isJsonArray())) {
jsonElement = jsonObject.get("data");
}
}
…
The reason why the data field of your ApiResponse was null is because your original deserializer was processing the whole response and making you "loose" the root object's data and meta elements.
I've also parametized the ApiResponse class:
public class ApiResponse<T> {
public Meta meta;
public T data;
}
That way deserializing still works without creating many different Response classes, while casting isn't needed anymore and you can specify the type of ApiResponse's data field as needed (eg. ApiResponse<User> for single user resource, ApiResponse<List<User>> for multiple user resources, etc.).

Create complex json objects using gson

How to create javabean for gson for the below JSON script?
{
"header": [
{
"title": {
"attempts": 3,
"required": true
}
},
{
"on": {
"next": "abcd",
"event": "continue"
}
},
{
"on": {
"next": "",
"event": "break"
}
}
]
}
I'm trying to build the javabean for this JSON output. I'm not able to repeat the fieldname on.
Please suggest any solutions.
You will need multiple classes to accomplish this. I made some assumptions with the naming, but these should suffice:
public class Response {
private List<Entry> header;
private class Entry {
private Title title;
private On on;
}
private class Title {
int attempts;
boolean required;
}
private class On {
String next, event;
}
}
You can test it with a main() method like:
public static void main(String[] args) {
// The JSON from your post
String json = "{\"header\":[{\"title\":{\"attempts\":3,\"required\":true}},{\"on\":{\"next\":\"abcd\",\"event\":\"continue\"}},{\"on\":{\"next\":\"\",\"event\":\"break\"}}]}";
Gson gson = new GsonBuilder().setPrettyPrinting().create();
Response response = gson.fromJson(json, Response.class);
System.out.println(response.header.get(0).title.attempts); // 3
System.out.println(response.header.get(1).on.next); // abcd
System.out.println(gson.toJson(response)); // Produces the exact same JSON as the original
}

Dynamically created JSON Object in Java

I'm trying to build a JSON object in my servlet.
The object should look like this:
{
"firms": [
{
"name": "firm1",
"projects": [
{
"name": "firm1project1"
},
{
"name": "firm1project2"
},
{
"name": "firm1project3"
}
]
},
{
"name": "firm2",
"projects": [
{
"name": "firm2project1"
},
{
"name": "firm2project2"
},
{
"name": "firm2project3"
}
]
},
{
"name": "firm3",
"projects": [
{
"name": "firm3project1"
},
{
"name": "firm3project2"
},
{
"name": "firm3project3"
}
]
},
{
"name": "firm4",
"projects": [
{
"name": "firm4project1"
},
{
"name": "firm4project2"
},
{
"name": "firm4project3"
}
]
}
]
}
I have a problem in creating array of project names objects:
[
{
"name": "firm2project1"
},
{
"name": "firm2project2"
},
{
"name": "firm2project3"
}
]
Right now I have the code as showed below (oJsonInner is a JSONObject object, aProjects - ArrayList of JSONObject type). I build the oJsonInner object from the results I get from database query:
while(result.next()){
oJsonInner.put("name",result.getString("project_name"));
aProjects.add(oJsonInner);
}
Is there any way to get the value of the oJsonInner object in aProjects.add(oJsonInner); so during the next loop I could create a new oJsonInner object with different "project_name" value without updating the object that got into aProjects array during the first loop?
while(result.next()){
oJsonInner = new JsonObject();
oJsonInner.put("name",result.getString("project_name"));
aProjects.add(oJsonInner);
}
you can use a JSONArray object and add it JSON object.
while(result.next()){
JSONObject oJsonInner = new JSONObject();
JSONArray arr = new JSONArray();
json.put("name",result.getString("project_name"));
arr.put(json);
}
Try this method:
ArrayList<JSONObject> aProjects = new <JSONObject>ArrayList();
while(result.next()){
JSONObject oJsonInner = new JSONObject();
oJsonInner.put("name","project1");
aProjects.add(oJsonInner);
}
RESULT:
[{"name":"project1"}, {"name":"project2"}]
Use Gson lib that help you to serialize json format which take java bean and convert it to json format.
Model
public class Firm {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
private List<Project> projetcts;
public List<Project> getProjetcts() {
return projetcts;
}
public void setProjetcts(List<Project> projetcts) {
this.projetcts = projetcts;
}
public static class Project{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
}
Gson code
public static void main(String[] args) {
Firm [] firms = new Firm[2];
Project p1 = new Project();
p1.setName("project 1");
Project p2 = new Project();
p2.setName("project 2");
Project p3 = new Project();
p3.setName("project 3");
List<Project> projects = new ArrayList<Firm.Project>();
projects.add(p1);
projects.add(p2);
projects.add(p3);
Firm firm1 = new Firm();
firm1.setName("firm1");
firm1.setProjetcts(projects);
Firm firm2 = new Firm();
firm2.setName("firm2");
firm2.setProjetcts(projects);
firms[0] = firm1;
firms[1] = firm2;
String jsonText = new Gson().toJson(firms);
System.out.println(jsonText);
}
Result Sample
[{"name":"firm1","projetcts":[{"name":"project 1"},{"name":"project
2"},{"name":"project
3"}]},{"name":"firm2","projetcts":[{"name":"project
1"},{"name":"project 2"},{"name":"project 3"}]}]
Check this solution, it may satisfy your expectations:
https://stackoverflow.com/a/74174150/2609399
UPDATE:
In your case, the needed JSON object can be created like so (note that actual key-value lines can be imported from external files or other sources like socked, db and so on):
System.out.println(
JsonBuilder.of()
.add("[0].name", "firm2project1")
.add("[1].name = firm2project2")
.add("[2].name: \"firm2project3\"")
.build()
.toPrettyString()
);
, which results to:
[
{
"name": "firm2project1"
},
{
"name": "firm2project2"
},
{
"name": "firm2project3"
}
]

Categories