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")
Related
I have data that looks like this:
{
"status": "success",
"data": {
"irrelevant": {
"serialNumber": "XYZ",
"version": "4.6"
},
"data": {
"lib": {
"files": [
"data1",
"data2",
"data3",
"data4"
],
"another file": [
"file.jar",
"lib.jar"
],
"dirs": []
},
"jvm": {
"maxHeap": 10,
"maxPermSize": "12"
},
"serverId": "134",
"version": "2.3"
}
}
}
Here is the function I'm using to prettify the JSON data:
public static String stringify(Object o, int space) {
ObjectMapper mapper = new ObjectMapper();
try {
return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(o);
} catch (Exception e) {
return null;
}
}
I am using the Jackson JSON Processor to format JSON data into a String.
For some reason the JSON format is not in the format that I need. When passing the data to that function, the format I'm getting is this:
{
"status": "success",
"data": {
"irrelevant": {
"serialNumber": "XYZ",
"version": "4.6"
},
"another data": {
"lib": {
"files": [ "data1", "data2", "data3", "data4" ],
"another file": [ "file.jar", "lib.jar" ],
"dirs": []
},
"jvm": {
"maxHeap": 10,
"maxPermSize": "12"
},
"serverId": "134",
"version": "2.3"
}
}
}
As you can see under the "another data" object, the arrays are displayed as one whole line instead of a new line for each item in the array. I'm not sure how to modify my stringify function for it to format the JSON data correctly.
You should check how DefaultPrettyPrinter looks like. Really interesting in this class is the _arrayIndenter property. The default value for this property is FixedSpaceIndenter class. You should change it with Lf2SpacesIndenter class.
Your method should looks like this:
public static String stringify(Object o) {
try {
ObjectMapper mapper = new ObjectMapper();
DefaultPrettyPrinter printer = new DefaultPrettyPrinter();
printer.indentArraysWith(new Lf2SpacesIndenter());
return mapper.writer(printer).writeValueAsString(o);
} catch (Exception e) {
return null;
}
}
I don't have enough reputation to add the comment, but referring to the above answer Lf2SpacesIndenter is removed from the newer Jackson's API (2.7 and up), so instead use:
printer.indentArraysWith(DefaultIndenter.SYSTEM_LINEFEED_INSTANCE);
Source of the solution
This is my JSON Data :
{
"Main": {
"country": "USA",
"State": California,
"City": San Jose
},
"transport": [{
"id": 711,
"Train": "yes",
"Bus": "yes",
"CarPool": "Yes"
}],
}
This is my Model class :
public class MainPoint {
public String getCountry() {
return country;
}
public String getTrain() {
return train;
}
}
I am getting data in this way in another class :
public class MyData {
MainPoint mPoint;
public String getCountry {
if (mPoint.getCountry() != null) {
return mPoint.getCountry();
}
}
I want to add code here for setting superscript on getcountry value as 1 when getTrain response is Yes.
Superscript is formatting, you're going to have to take care of that in the presentation layer, not the data layer.
I am reading an REST API in JSON format. When reading the JSON i cannot extract the leaves with JSONPath. So what i would like to do is run thru the JSON string with Java and get the values i need, they are always in the same order. This is the JSON string i need to extract values from:
{
"10516": {
"estimated": {
"value": 10.0,
"text": "10.0"
},
"completed": {
"value": 7.5,
"text": "7.5"
}
},
"10244": {
"estimated": {
"value": 15.5,
"text": "15.5"
},
"completed": {
"value": 7.5,
"text": "7.5"
}
},
"10182": {
"estimated": {
"value": 12.0,
"text": "12.0"
},
"completed": {
"value": 10.0,
"text": "10.0"
}
},
"10391": {
"estimated": {
"value": 16.0,
"text": "16.0"
},
"completed": {
"value": 3.0,
"text": "3.0"
}
},
"10183": {
"estimated": {
"value": 12.0,
"text": "12.0"
},
"completed": {
"value": 7.0,
"text": "7.0"
}
},
"10123": {
"estimated": {
"value": 11.5,
"text": "11.5"
},
"completed": {
"value": 5.5,
"text": "5.5"
}
},
"10447": {
"estimated": {
"value": 7.0,
"text": "7.0"
},
"completed": {
"value": 3.0,
"text": "3.0"
}
}}
As you can see the ID 10516 has estimated and completed, i would like to extract those values for each ID. So the output should look like this:
ID | ESTIMATED | COMPLETED
10516 | 10.0 | 7.5
10244 | 15.5 | 7.5
10182 | 12.0 | 10.0
and so on..
it is important that for every ID the values are in the same row because i need to upload them to my PostgreSQL db and join this data with other data on the ID.
My idea is to coun't the values surrounded by quotation marks and get the 1st, 5th, 9th, 10th, 14th and 18th value and so on. because the JSON string is always in the same order i guess it could be done like that..
please help out and keep in mind i don't have any experience in java at all and the implementation will be done in Talend Open Studio. also see my other question that raised this question because JSONPath cannot help me: Extract leaves from JSON file with JSONpath
what JSON parser will help me extract the leaves (10516,10244 etc.)?
Examples with Jackson (com.fasterxml.jackson.core:jackson-databind:2.0.6):
The quick and dirty approach:
String json = "{\n" +
"\"10516\": {\"estimated\": {\"value\": 10.0,\"text\": \"10.0\"},\"completed\": {\"value\": 7.5,\"text\": \"7.5\"}},\n" +
"\"10244\": {\"estimated\": {\"value\": 15.5,\"text\": \"15.5\"},\"completed\": {\"value\": 7.5,\"text\": \"7.5\"}},\n" +
"\"10182\": {\"estimated\": {\"value\": 12.0,\"text\": \"12.0\"},\"completed\": {\"value\": 10.0,\"text\": \"10.0\"}}\n" +
"}";
ObjectMapper mapper = new ObjectMapper();
TypeReference<Map> typeRef = new TypeReference<Map>(){};
try {
Map<String, Object> map = mapper.readValue(json, typeRef);
System.out.println(map);
} catch (IOException e) {
// log error
}
Output:
{10516={estimated={value=10.0, text=10.0}, completed={value=7.5, text=7.5}}, 10244={estimated={value=15.5, text=15.5}, completed={value=7.5, text=7.5}}, 10182={estimated={value=12.0, text=12.0}, completed={value=10.0, text=10.0}}}
This way you can parse any JSON string. You can of course access all JSON fields as long as you cast to the corresponding type:
Map item = (Map) map.get("10182");
System.out.println(item);
Map estimated = (Map) item.get("estimated");
Double value = (Double) estimated.get("value");
System.out.println(value);
String text = (String) estimated.get("text");
double newValue = value + 10;
System.out.println("old text: "+text+", new value: "+newValue);
Output:
{estimated={value=12.0, text=12.0}, completed={value=10.0, text=10.0}}
12.0
old text: 12.0, new value: 22.0
The cleaner, better approach
Of course, a better approach would be to define some model classes like the following:
class Model {
ModelContent estimated;
public ModelContent getEstimated() {return estimated;}
public void setEstimated(ModelContent estimated) { this.estimated = estimated;}
ModelContent completed;
public ModelContent getCompleted() {return completed;}
public void setCompleted(ModelContent completed) { this.completed = completed;}
public Model() {}
}
class ModelContent {
Double value;
public Double getValue() {return value;}
public void setValue(Double value) { this.value = value;}
String text;
public String getText() {return text;}
public void setText(String text) {this.text = text;}
public ModelContent() {}
}
And use them as the value type for the root map:
ObjectMapper mapper = new ObjectMapper();
TypeReference<Map<String,Model>> typeRef = new TypeReference<Map<String,Model>>(){};
try {
Map<String, Model> map = mapper.readValue(json, typeRef);
Model item = map.get("10182");
ModelContent completed = item.getCompleted();
Double completedValue = completed.getValue();
System.out.println("value: "+completedValue);
} catch (IOException e) {
// log error
}
Output:
value: 10.0
String jsonString="YOUR JSON STRING";
JSONObject mainObject=new JSONObject(jsonString);
Iterator<String> keys= mainObject.keys();
while (keys.hasNext())
{
String keyValue = (String)keys.next();
JSONObject obj1 = mainObject.getJSONObject(keyValue);
JSONObject estimatedObj = obj1.getJSONObject("estimated");
JSONObject completedObj = obj1.getJSONObject("completed");
System.out.print(keyvalue+"-"+estimatedObj.getString("text")+"-"+completedObj.getString("text"));
System.out.println();
}
I'm trying to deserialize a JSON object (from JIRA REST API createMeta) with unknown keys.
{
"expand": "projects",
"projects": [
{
"self": "http://www.example.com/jira/rest/api/2/project/EX",
"id": "10000",
"key": "EX",
"name": "Example Project",
"avatarUrls": {
"24x24": "http://www.example.com/jira/secure/projectavatar?size=small&pid=10000&avatarId=10011",
"16x16": "http://www.example.com/jira/secure/projectavatar?size=xsmall&pid=10000&avatarId=10011",
"32x32": "http://www.example.com/jira/secure/projectavatar?size=medium&pid=10000&avatarId=10011",
"48x48": "http://www.example.com/jira/secure/projectavatar?pid=10000&avatarId=10011"
},
"issuetypes": [
{
"self": "http://www.example.com/jira/rest/api/2/issueType/1",
"id": "1",
"description": "An error in the code",
"iconUrl": "http://www.example.com/jira/images/icons/issuetypes/bug.png",
"name": "Bug",
"subtask": false,
"fields": {
"issuetype": {
"required": true,
"name": "Issue Type",
"hasDefaultValue": false,
"operations": [
"set"
]
}
}
}
]
}
]
}
My problem is: I don't know the keys into "fields" (in the example below "issuetype", "summary", "description", "customfield_12345").
"fields": {
"issuetype": { ... },
"summary": { ... },
"description": { ... },
"customfield_12345": { ... }
}
It would be awesome if I could deserialize it as an array with the key as "id" in my POJO so the above example will looke like the following:
class IssueType {
...
public List<Field> fields;
...
}
class Field {
public String id; // the key from the JSON object e.g. "issuetype"
public boolean required;
public String name;
...
}
Is there a way I can achieve this and wrap in my model? I hope my problem is somehow understandable :)
If you don't know the keys beforehand, you can't define the appropriate fields. The best you can do is use a Map<String,Object>.
If there are in fact a handful of types, for which you can identify a collection of fields, you could write a custom deserializer to inspect the fields and return an object of the appropriate type.
I know it's old question but I also had problem with this and there are results..
Meybe will help someone in future : )
My Response with unknow keys:
in Model Class
private JsonElement attributes;
"attributes": {
"16": [],
"24": {
"165": "50000 H",
"166": "900 lm",
"167": "b.neutr.",
"168": "SMD 3528",
"169": "G 13",
"170": "10 W",
"171": "230V AC / 50Hz"
}
},
So I also checked if jsonElement is jsonArray its empty.
If is jsonObject we have data.
ProductModel productModel = productModels.get(position);
TreeMap<String, String> attrsHashMap = new TreeMap<>();
if (productModel.getAttributes().isJsonObject())
{
for (Map.Entry<String,JsonElement> entry : productModel.getAttributes().getAsJsonObject().entrySet())
{
Log.e("KEYS", "KEYS: " + entry.getKey() + " is empty: " + entry.getValue().isJsonArray());
if (entry.getValue() != null && entry.getValue().isJsonObject())
{
for (Map.Entry<String, JsonElement> entry1 : entry.getValue().getAsJsonObject().entrySet())
{
Log.e("KEYS", "KEYS INSIDE: " + entry1.getKey() + " VALUE: " + entry1.getValue().getAsString());
// and there is my keys and values.. in your case You can get it in upper for loop..
}
}
}
There is a perfectly adequate JSON library for Java that will convert any valid JSON into Java POJOs. http://www.json.org/java/
I have this json as a string (this is from google custom search). I have made equivalent java classes that are neccessary to parse this. I use gson and am getting a NullPointerException.
"items": [
{
"kind": "customsearch#result",
"title": "A Florida Road Trip - Articles | Travel + Leisure",
"htmlTitle": "A \u003cb\u003eFlorida\u003c/b\u003e Road Trip - \u003cb\u003eArticles\u003c/b\u003e | Travel + Leisure",
"link": "http://www.travelandleisure.com/articles/paradise-usa",
"displayLink": "www.travelandleisure.com",
"snippet": "On a road trip across Florida, Karrie Jacobs goes in search of the authentic amid New Urbanist experiments in postmodern nostalgia.",
"htmlSnippet": "On a road trip across \u003cb\u003eFlorida\u003c/b\u003e, Karrie Jacobs goes in search of the authentic amid \u003cbr\u003e New Urbanist experiments in postmodern nostalgia.",
"cacheId": "QrfYt6p-U3gJ",
"formattedUrl": "www.travelandleisure.com/articles/paradise-usa",
"htmlFormattedUrl": "www.travelandleisure.com/\u003cb\u003earticles\u003c/b\u003e/paradise-usa",
"pagemap": {
"cse_image": [
{
"src": "http://static0.travelandleisure.com/images/amexpub/0000/6261/200702_urban.jpg"
}
],
"cse_thumbnail": [
{
"width": "160",
"height": "200",
"src": "https://encrypted-tbn1.gstatic.com/images?q=tbn:ANd9GcR_Jhmej45Lu7L3bQoKz1poITJ5ZeuVvKhH62myfBOj-1VEX9oqwIhvH74"
}
],
"metatags": [
{
"fb:app_id": "145695458805001",
"og:url": "http://www.travelandleisure.com/articles/paradise-usa",
"og:site_name": "Travel + Leisure",
"og:image": "http://static0.travelandleisure.com/images/amexpub/0000/6261/200702_urban.jpg",
"og:type": "article",
"og:title": "Driving: Florida’s New Urbanist Experiments",
"og:description": "On a road trip across Florida, Karrie Jacobs goes in search of the authentic amid New Urbanist exper..."
}
]
}
}
And my equivalent java classes are as follows:
public Class Mapper
{
public Items[] items;
}
public Class Items
{
public String title;
public String htmlSnippet;
public String link;
public Pagemap pagemap;
}
public Class Pagemap
{
public Cse_image[] image;
}
public Class Cse_image
{
public String src;
}
public Class Parser
{
public static void main(String[] args)
{
//assume json is the string representation of the above json
Gson gson = new GsonBuilder().create();
Mapper mapper= gson.fromJson(json,Mapper.class);
System.out.println("src here"+mapper.items[0].pagemap.image[0].src);
}
}
The above code throws me a NullPointerException , but if I print till the pagemap ,I get an object with a valid hashcode. Any pointers?
Whenever dealing with JSON, it is a good practice to use a LINT software to verify the contents of JSON. I recommend using jsonlint.com. The JSON list shows multiple errors in the JSON string. Running some REPL cycles here is the corrected JSON.
{
"items": [
{
"kind": "customsearch#result",
"title": "AFloridaRoadTrip-Articles|Travel+Leisure",
"htmlTitle": "A<b>Florida</b>RoadTrip-<b>Articles</b>|Travel+Leisure",
"link": "http: //www.travelandleisure.com/articles/paradise-usa",
"displayLink": "www.travelandleisure.com",
"snippet": "OnaroadtripacrossFlorida,KarrieJacobsgoesinsearchoftheauthenticamidNewUrbanistexperimentsinpostmodernnostalgia.",
"htmlSnippet": "Onaroadtripacross<b>Florida</b>,KarrieJacobsgoesinsearchoftheauthenticamid<br>NewUrbanistexperimentsinpostmodernnostalgia.",
"cacheId": "QrfYt6p-U3gJ",
"formattedUrl": "www.travelandleisure.com/articles/paradise-usa",
"htmlFormattedUrl": "www.travelandleisure.com/<b>articles</b>/paradise-usa",
"pagemap": {
"cse_image": [
{
"src": "http: //static0.travelandleisure.com/images/amexpub/0000/6261/200702_urban.jpg"
}
],
"cse_thumbnail": [
{
"width": "160",
"height": "200",
"src": "https: //encrypted-tbn1.gstatic.com/images?q=tbn: ANd9GcR_Jhmej45Lu7L3bQoKz1poITJ5ZeuVvKhH62myfBOj-1VEX9oqwIhvH74"
}
],
"metatags": [
{
"fb: app_id": "145695458805001",
"og: url": "http: //www.travelandleisure.com/articles/paradise-usa",
"og: site_name": "Travel+Leisure",
"og: image": "http: //static0.travelandleisure.com/images/amexpub/0000/6261/200702_urban.jpg",
"og: type": "article",
"og: title": "Driving: Florida’sNewUrbanistExperiments",
"og: description": "OnaroadtripacrossFlorida,KarrieJacobsgoesinsearchoftheauthenticamidNewUrbanistexper..."
}
]
}
}
]
}
First rename your class Cse_Image to a more conventional java name (CseImage maybe?), same for the thumbnail class.
Then you can try genson library http://code.google.com/p/genson/.
Put #JsonProperty("cse_image") annotation on your image field (or on your setter/getter if you have) and it works :
Genson genson = new Genson();
Mapper mapper= genson.deserialize(json,Mapper.class);
System.out.println("src here"+mapper.items[0].pagemap.image[0].src);