I'm having some trouble as I've hit a roadblock in learning how to parse JSON with json simple.
To make myself concise;
I am trying to parse this bit of JSON from a url
"hourly": {
"summary": "Clear throughout the day.",
"icon": "clear-day",
"data": [
{
"time": 1550379600,
"summary": "Clear",
"icon": "clear-day",
"precipIntensity": 0,
"precipProbability": 0,
"temperature": 20.18,
"apparentTemperature": 14.31,
"dewPoint": 13.79,
"humidity": 0.76,
"pressure": 1024.47,
"windSpeed": 4.08,
"windGust": 5.25,
"windBearing": 30,
"cloudCover": 0.07,
"uvIndex": 0,
"visibility": 10,
"ozone": 342.67
}
so, in using json simple, this is how I am parsing this JSON
try{
String genreJson = IOUtils.toString(new URL(url));
JSONObject genreJsonObject = (JSONObject) JSONValue.parseWithException(genreJson);
//get the title
System.out.println(genreJsonObject.get("hourly")); //THIS WORKS
//System.out.println(genreJsonObject.get("visibility"));
//get the data
JSONArray genreArray = (JSONArray) genreJsonObject.get(0);
//get the first genre
//JSONObject firstGenre = (JSONObject) genreArray.get(0);
//System.out.println(firstGenre.get("data"));
}
catch (IOException | ParseException e){
e.printStackTrace();
}
So, in calling System.out.println(genreJsonObject.get("hourly")); I get everything within the brackets titled "hourly". My intent is to parse for the data within the "hourly" bracket, although I can't figure out how to parse for a title within a titled bracket. Specifically, I need the time, the precipProbability, precipIntensity, and precipProbability (this attribute is not in the example).
I apologize for any lack of detail as I'm doing this as part my first hackathon, and I'm trying not to fall asleep right now.
I greatly appreciate anyone's help.
This is object to describe your json "data" part:
class Data {
private Long time;
private String summary;
private String icon;
private Double dewPoint;
// add all the fields you need
// add empty constructor + getters and setters
}
if you want to skip some of the fields in "data" which you don't care about use
#JsonIgnoreProperties(ignoreUnknown = true)
class Data{
...
}
Your parent object is "hourly", so:
class Hourly {
private String summary;
private String icon;
private List<Data> data;
// add all the fields you need
// add empty constructor + getters and setters
}
At this point the Java POJO classes describe the Json data, so you're ready to map the JSON to the Java classes.
To read this with Jackson, you will need the String representation of the Json (without any URL parts), so let's assume you can do that in a string called "inputJsonString".
Reading this with Jackson will be as simple as:
ObjectMapper mapper = new ObjectMapper();
Hourly hourly = mapper.readValue(inputJsonString, Hourly.class);
Related
I have this String Json Payload
[
"key1":{
"atr1":"key1",
"atr2":"value1",
"atr3":"value2",
"atr4":"value3,
"atr5":"value4"
},
"key2":{
"atr1":"key2",
"atr2":"value5",
"atr3":"value6",
"atr4":value7,
"atr5":"value8"
}
]
and I want it to be converted in to the following format using Java
[
{
"atr2":"value1",
"atr3":"value2",
"atr4":"value3,
"atr5":"value4"
},
{
"atr2":"value5",
"atr3":"value6",
"atr4": "value7",
"atr5":"value8"
}
]
What would be the simplest way of transforming this ?
You cannot, because the example below is not valid json.
Check it out using this JSON validator.
If you paste this in (I've fixed some basic errors with lack of quotes)
{
{
"atr2":"value1",
"atr3":"value2",
"atr4":"value3",
"atr5":"value4"
},
{
"atr2":"value5",
"atr3":"value6",
"atr4":"value7",
"atr5":"value8"
}
}
You will get these errors ...
It can work if you change the target schema to something like this by using a json-array to contain your data.
[
{
"atr2":"value1",
"atr3":"value2",
"atr4":"value3",
"atr5":"value4"
},
{
"atr2":"value5",
"atr3":"value6",
"atr4":"value7",
"atr5":"value8"
}
]
If this works for you, then this problem can easily be solved by using the ObjectMapper class.
You use it to deserealize the original JSON into a class, which has two fields "key1" and "key2"
Extract the values of these fields and then just store them in an array ...
Serialize the array using the ObjectMapper.
Here a link, which explains how to use the ObjectMapper class to achieve the goals above.
EDIT:
So you'll need the following classes to solve the problem ...
Stores the object data
class MyClass {
String atr2;
String art3;
}
Then you have a container class, which is used to store the initial json.
class MyClassContainer {
MyClass key1;
MyClass key2;
}
Here's how you do the parse from the original json to MyClassContainer
var mapper = new ObjectMapper()
var json = //Get the json String somehow
var myClassContainer = mapper.readValue(json,MyClassContainer.class)
var mc1 = myClassContainer.getKey1();
var mc2 = myClassContainer.getKey2();
var myArray = {key1, key2}
var resultJson = mapper.writeValueAsString(myArray)
Assuming that you will correct the JSON into a valid one (which involves replacing the surrounding square braces with curly ones, and correct enclosure of attribute values within quotes), here's a simpler way which involves only a few lines of core logic.
try{
ObjectMapper mapper = new ObjectMapper();
mapper.configure( DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false );
HashMap<String, Data> map = mapper.readValue( jsonString, new TypeReference<HashMap<String, Data>>(){} );
String json = mapper.writeValueAsString( map.values() );
System.out.println( json );
}
catch( JsonProcessingException e ){
e.printStackTrace();
}
jsonString above is your original JSON corrected and valid JSON input.
Also notice the setting of FAIL_ON_UNKNOWN_PROPERTIES to false to allow atr1 to be ignored while deserializing into Data.
Since we are completely throwing away attr1 and its value, the Data class will represent all fields apart from that.
private static class Data{
private String atr2;
private String atr3;
private String atr4;
private String atr5;
}
I have a huge JSON but I only need to parse specific fields. I know paths to these fields so I decided to try JPath and it works but I want to parse all fields at once.
Let's say I have such JSON:
{
"data": [
{
"field1": 1,
"field2": 1,
...
"another_data": [ {
"required_field1": "1",
"required_field2": "2"
}
],
...
}
]
}
I want to get only required fields with these paths and map it to Java POJO:
$.data[*].another_data[*].required_field1
$.data[*].another_data[*].required_field2
So as a final result I want to have a list of Java objects, where the object contains required_field1 and required_field2.
UPD:
how it works now
I have a Java POJO that's a container
class RequiredInfo {
private String field1;
private String field2;
//constructor, setters, etc
}
I read JSON path 2 times by using JPath:
String json = "...";
Object document = Configuration.defaultConfiguration().jsonProvider().parse(json);
List<String> reqFields1 = JsonPath.read(document, "$.data[*].another_data[*].required_field1");
List<String> reqFields2 = JsonPath.read(document, "$.data[*].another_data[*].required_field2")
and then I map it to my POJO
for (int i = 0; i < reqFields1.size(); i++) {
res.add(new RequiredInfo(reqFields1.get(i), reqFields2.get(i)));
}
bit I think there is a better way how I can do it
You can try by creating a JSON object and get data:
JSONObject yourJsonObject = (JSONObject) new JSONParser().parse(yourJson);
JSONObject data = (JSONObject) yourJsonObject.get("data");
JSONObject data0 = (JSONObject) data.get(0);
JSONObject another_data = (JSONObject) data0.get("another_data");
String required_field1 = another_data.get("required_field1").toString();
String required_field2 = another_data.get("required_field2").toString();
Now that you have values, you can add them in your POJOs.
I have below json:
{
"type":Flowers,
"input_data": [
{
"id": 35742,
"Request_ID": 8383,
"data_line": "*****Sample text here*****",
"variety": {
"Rose": 0,
"Jasmine": 0,
"Lily": 1,
"Sunflower": 1,
},
"responded": 1
},
{
"id": 35992,
"Request_ID": 8983,
"data_line": "*****Sample text here*****",
"variety": {
"Rose": 1,
"Jasmine": 0,
"Lily": 0,
"Sunflower": 1,
},
"responded": 1
}
],
"token": "F9500930C-15A6-4111-AD7F-7D0DF0CEE4D8"
}
How do I map the values in "variety" with "id"?
Note: id is coming from the response of a different API which should be replaced in this json and mapped with variety.
It's not a good idea to have a field like 'input_data' in your json.
Try to redo your json that's aligned to your data model and something that can be mapped to Java objects.
GSON is a great library for dealing with JSONs in Java - https://github.com/google/gson
I can give you a utility method to get a list of Java objects from a json reprenstation like this:
private static Type typeOfT = TypeToken.getParameterized(List.class, <<your-class>>.class).getType();
public static <T> List<T> loadListFromFile(String fileName, Class<T> clazz) throws Exception{
File file = new File(fileName);
String json = new String(Files.readAllBytes(file.toPath()));
return gson().fromJson(json, typeOfT);
}
First, your JSON string is invalid (e.g. Flowers should be quoted by double quotes). Second, I am confused about what you really want. Therefore, according to the comment under your question, you said that you need a mapping between id and variety. Here comes one way to achieve that by following steps with Jackson (One of the most popular JSON libraries.).
Step 1
Create nested POJOs for deserialization.
class Result {
#JsonProperty("input_data")
private List<InputData> inputData;
//general getter and setter
}
class InputData {
private int id;
private Variety variety;
//general getters and setters
}
class Variety {
#JsonProperty("Rose")
private int rose;
#JsonProperty("Jasmine")
private int jasmine;
#JsonProperty("Lily")
private int lily;
#JsonProperty("Sunflower")
private int sunflower;
//general getters, setters and toString()
}
Step 2
Deserialize JSON response to Result and transform the InputData into Map<Integer, Variety> with Java 8.
ObjectMapper mapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
Result result = mapper.readValue(jsonStr, Result.class);
Map<Integer, Variety> idVarietyMap = result.getInputData().stream()
.collect(Collectors.toMap(InputData::getId, InputData::getVariety));
System.out.println(idVarietyMap.toString());
Console output
{35992=Variety [rose=1, jasmine=0, lily=0, sunflower=1], 35742=Variety [rose=0, jasmine=0, lily=1, sunflower=1]}
I have a Java POJO
public class TagBean {
private String type;
private String id;
public TagBean(String type, String id) {
this.type = type;
this.id = id;
}
// getters
// setters
}
I'm building pojo's and adding them to a List, as
....
List<TagBean> channelsList = new ArrayList<>();
List<TagBean> showsList = new ArrayList<>();
for each <business logic> {
if value=channels {
channelsList.add(new TagBean(...));
}
if value=shows {
showsList.add(new TagBean(...));
}
}
Gson gson = new GsonBuilder().create();
JsonObject tjsonObject = new JsonObject();
tjsonObject.addProperty("channels", gson.toJson(channelsList));
tjsonObject.addProperty("shows", gson.toJson(showsList));
JsonObject mainjsonObject = mainjsonObject.add("tags", tjsonObject);
return mainjsonObject;
My output is:
{
"tags": {
"channels": "[{\"type\":\"channel\",\"id\":\"channel\",\"name\":\"Channel\",\"parent\":\"SXM\"}]",
"shows": "[{\"type\":\"shows\",\"id\":\"shows\",\"name\":\"Shows\",\"parent\":\"SXM\"},{\"type\":\"shows\",\"id\":\"howard\",\"name\":\"Howard Stern\",\"parent\":\"shows\"},{\"type\":\"shows\",\"id\":\"howardstern\",\"name\":\"Howard Stern\",\"parent\":\"howard\"}]",
"sports": "[]"
}
}
How can i remove the backslashes? So the output is like:
{
"tags": {
"channels": " [{"type":"channel","id":"channel","name":"Channel","parent":"SXM"}]",
"shows": "[{"type":"shows","id":"shows","name":"Shows","parent":"SXM"},{"type":"shows","id":"howard","name":"Howard Stern","parent":"shows"}....
There were few other posts, but none explained this.
The problem is caused by this:
tjsonObject.addProperty("channels", gson.toJson(channelsList));
What that is doing is converting channelsList to a string containing a representation of the list in JSON, then setting the property to that string. Since the string contains JSON meta-characters, they must be escaped when the strings are serialized ... a second time.
I think that you need to do this instead:
tjsonObject.add("channels", gson.toJsonTree(channelsList));
That should produce this:
{
"tags": {
"channels":
[{"type":"channel","id":"channel","name":"Channel","parent":"SXM"}],
"shows":
[{"type":"shows","id":"shows","name":"Shows","parent":"SXM"},
{"type":"shows","id":"howard","name":"Howard Stern","parent":"shows"}
....
That is slightly different to what your question asked for, but it has the advantage of being syntactically valid JSON!
String mainJsonStr = mainjsonObject.toString();
mainJsonStr = mainJsonStr.replace("\\\\", ""); //replace the \
System.out.println(mainJsonStr);
The problem is that gson.toJson returns a String, and
tjsonObject.addProperty("channels", gson.toJson(channelsList));
this will add channels as a string and not as a JSON object.
One possible solution is to convert the string returned from gson.toJson to JSON object first then add it to the parent JSON object like
Gson gson = new GsonBuilder().create();
JsonObject tjsonObject = new JsonObject();
tjsonObject.put("channels", new JsonObject(gson.toJson(channelsList)));
tjsonObject.put("shows", new JsonObject(gson.toJson(showsList)));
this will treat channels and shows as JSON object
All strings in java have to escape quotes in them. So jsonInString should have slashes in it. When you output jsonInString though it shouldn't have the quotes. Are you looking at it in a debugger or something?
Just parse json directly and check - will get the output
above solution is not working anymore since GSON 2.8.*
use gson.toJsonTree(jsonText).getAsString(); instead
I parse server JSON response with GSON library. Backend guys sometimes tell me: "We can't specify variable type in JSON for some reason" (old php, they don't know how to do it and so on and so forth).
GSON likes strong typing in its object model. So I can't parse Object as String.
GSON wait for:
{
"service":{
"description":null,
"name":"Base",
"id":"4c7a90410529"
}
}
But it gets (empty data):
"service": ""
And I get
java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1396
What is the best practice to parse such response?
Another question:
How can I build object, it can recognize Integer variable which returned from time to time as Integer or as String? The same server side issue.
"data": "1"
or
"data": 1
I know - we should use specific types in Java. But sometime it is worth to make concessions,
Thanks
EDIT:
My solution based on Java Developer's answer.
ServiceDeserializer class deserialize every object depending on its internal value.
public class ServiceDeserializer implements JsonDeserializer<ServiceState>{
#Override
public ServiceState deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
String name = "";
String uuid = "";
String description = "";
if (json.isJsonObject()) {
JsonObject obj = json.getAsJsonObject();
if (!obj.get("name").isJsonNull()) {
name = obj.get("name").getAsString();
}
if (!obj.get("uuid").isJsonNull()) {
uuid = obj.get("uuid").getAsString();
}
if (!obj.get("description").isJsonNull()) {
description = obj.get("description").getAsString();
}
}
return new ServiceState(name, uuid, description);
}
}
And my GSON constructor with type adapter for ServiceState.
Gson gson = new GsonBuilder()
.registerTypeAdapter(ServiceState.class, new ServiceDeserializer())
.create();
You need to scrape the JSON response before trying to deserialize it into your Response Java object. You can make use of Java org.json parser to verify that service object actually exists and fix it otherwise.
String json = "{\"service\":{\r\n" +
" \"description\":null,\r\n" +
" \"name\":\"Base\",\r\n" +
" \"id\":\"4c7a90410529\"\r\n" +
"}}";
String json2 = "{\"service\":\"\"}";
JSONObject root = new JSONObject(json);
// JSONObject root = new JSONObject(json2);
if (root.optJSONObject("service") == null) {
root.put("service", new JSONObject());
}
Gson gson = new Gson();
Response response = gson.fromJson(root.toString(), Response.class);
System.out.println(response.getService());
Output :
// for JSONObject root = new JSONObject(json);
Service [id=4c7a90410529, name=Base, description=null]
// for JSONObject root = new JSONObject(json2);
Service [id=null, name=null, description=null]
Secondly, Gson is smart enough to do simple conversions like String to Integer etc. So, deserializing such JSON properties shouldn't give you any troubles.
System.out.println(gson.fromJson("10", Integer.class)); // 10
System.out.println(gson.fromJson("\"20\"", Integer.class)); // 20
If you want to stick with strictly gson you can provide a custom deserializer. Since we know that service is either a property of the base json string or embedded within some other property, we can use the deserializer to step-wise parse out the offending components and handle them accordingly.
public class MyJsonDeserializer implements JsonDeserializer<YourParsedData> {
#Override
public YourParsedData deserialize(final JsonElement je, final Type type, final JsonDeserialization Context jdc) throws JsonParseException
{
final JsonObject obj = je.getAsJsonObject(); //our original full json string
final JsonElement serviceElement = obj.get("service");
//here we provide the functionality to handle the naughty element. It seems emtpy string is returned as a JsonPrimitive... so one option
if(serviceElement instanceOf JsonPrimitive)
{
//it was empty do something
}
return YourParsedData.create(); //provide the functionality to take in the parsed data
}
}
The custom deserializer would be called as follows:
final Gson gson = new GsonBuilder().registerTypeAdapter(YourParsedData.class, new MyJsonDeserializer()).create();
gson.fromJson("{service: ''}", YourParsedData.class);
I typed all this up so if I missed some syntax my apologies.
Your json is invalid and any Json parser wouldn't be able to parse a syntactically incorrect json:
"service": {
"description": null,
"name": "Base",
"id": "4c7a90410529"
}
should be encapsulated in curly braces as mentioned here:
{
"service": {
"description": null,
"name": "Base",
"id": "4c7a90410529"
}
}
A json structure is enclosed within {}. Your response seems to be missing that. You can manually append { and } at the beginning and end of the string to make it into a valid json structure.
Once this is done, you can use Gson to parse your json response normally.
What is the best practice to parse such response?
Use a good enough Json parser. That's more than enough. And try to have a class representing the exact same Structure as the response to avoid parsing the json responses level by level, manually.