Manipulate JSON with Gson [duplicate] - java

This question already has an answer here:
Expected BEGIN_ARRAY but was STRING but JSON is correct
(1 answer)
Closed 1 year ago.
I have the following problem. I have a Writer class which writes me all objects I create into a JSON file via Gson. The output looks like that:
{
"eventID": 1,
"title": "one",
"timeCreated": "Sep 23, 2007, 10:10:00 AM",
"timeModified": "Sep 23, 2007, 10:10:00 AM",
"timeStart": "Nov 11, 2999, 11:11:00 AM",
"timeEnd": "Nov 11, 3999, 11:11:00 AM",
"alarmOn": true,
"content": ""
}
eventID is a serialized integer created with AtomicInteger each time a new obj get created.
I want to manipulate those object e.g., set a new title.
So in the first step I tried to get the JSON back into java via fromJson
Gson gson = new Gson();
Type myDataType = new TypeToken<Collection<Event>>() {
}.getType();
Collection<Event> myData = gson.fromJson("awesome.json", myDataType);
for (Event e : myData) {
System.out.println("value= " + e.getTitle());
}
However, this leads to an error
Exception in thread "main" com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was STRING at line 1 column 1 path $
How do I properly read my json back to my java class, apply some changes and write it after back again to json?

Let's look at the error code
Exception in thread "main" com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was STRING at line 1 column 1 path $
So, it seems that the library is expecting a json array, but you give it a String instead.
It most probably has to do with this like new TypeToken<Collection<Event>>() you are saying that your type is a collection of events, not a single event.
Try changing it to TypeToken<Event> and see if this works.

Related

Iterating through a JSONArray in Java in order to load them into variables

Context: using an API call using OKHTTP so that I can request some information for simple pieces of information (it will be a URL link, and then multiple pieces of text such as the "Track_Name"). Once this info is received, I'm setting it to a load of variables that can be used later.
However, this is where the problem is coming from, as I have only ever had to deal with JSONObjects, not JSONArrays, and therefore don't know how to iterate through them.
I've seen there are two types of For loops, the enhanced for and the for loop. Can someone please advise how I should be approaching this?
This is what my JSON file roughly looks like:
"response": {
"results": [
{
"Track": "https://exampletrackaddress.com",
"Track_Name": "Example track name",
"Created By": "111111111111",
"Track_Code": "YYYYY",
"Public": true,
"_id": "12121212121212121212"
}
],
"cursor": 0,
"count": 1,
"remaining": 0
}
}
I've tried running the integer parse that Android Studio recommends but it obviously doesn't help (bit of a long shot!).
private CurrentTrack getCurrentDetails (String jsonData) throws JSONException {
JSONObject track = new JSONObject(jsonData);
JSONObject trackArray = track.getJSONObject("response");
JSONArray trackArrayLowerLevel = trackArray.getJSONArray("results");
CurrentTrack currentTrack = new CurrentTrack();
// assigning the variables of the current track so we have the details
currentTrack.setTrackCode(trackArrayLowerLevel.getString(Integer.parseInt("Track_Code")));
currentTrack.setTrackLink(trackArrayLowerLevel.getString(Integer.parseInt("Track")));
currentTrack.setTrackName(trackArrayLowerLevel.getString(Integer.parseInt("Track_Name")));
currentTrack.setPublicTrack(trackArrayLowerLevel.getBoolean(Integer.parseInt("Public")));
return currentTrack;
}
I'm expecting the result to be an item for each entry that I can use to assign to variables.
ERROR MESSAGE:
2019-07-21 19:29:50.944 6593-7239/com.example.hypnostream E/AndroidRuntime: FATAL EXCEPTION: OkHttp Dispatcher
Process: com.example.hypnostream, PID: 6593
java.lang.NumberFormatException: For input string: "Track_Code"
at java.lang.Integer.parseInt(Integer.java:615)
at java.lang.Integer.parseInt(Integer.java:650)
at com.example.hypnostream.ListenScreen.getCurrentDetails(ListenScreen.java:132)
at com.example.hypnostream.ListenScreen.access$200(ListenScreen.java:29)
at com.example.hypnostream.ListenScreen$1.onResponse(ListenScreen.java:81)
at okhttp3.RealCall$AsyncCall.run(RealCall.kt:138)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:919)

Google Gson treats every JSON local file as a string even when its not

I am constantly getting Expected BEGIN_TYPE but was STRING at line 1 column 1 path $ error. I've read about that error, but I am experiencing something different.
When I try to use gson.fromJson() on a JSON string I've created in my app it compiles fine.
ArrayList<MyCar> cars = new ArrayList<>();
cars.add(new MyCar());
cars.add(new MyCar());
String json = gson.toJson(cars);
This compiles.
Type carList = new TypeToken<ArrayList<MyCar>>(){}.getType();
ArrayList<MyCar> myCars = gson.fromJson(json, carList);
This compiles as well.
My problem is when I try to read from a local file I've either written myself or downloaded from the web (I have run all local files on JsonLint and they're valid).
Here is the JSON when written to a file named testingArray.json:
[{
"model": "I3",
"manufacturer": "Audi",
"features": ["wifi", "bluetooth", "charging"]
}, {
"model": "I3",
"manufacturer": "Audi",
"features": ["wifi", "bluetooth", "charging"]
}, {
"model": "I3",
"manufacturer": "Audi",
"features": ["wifi", "bluetooth", "charging"]
}]
It clearly begins with brackets and not quotes.
But this:
Type carList = new TypeToken<ArrayList<MyCar>>(){}.getType();
ArrayList<MyCar> myCars = gson.fromJson(basePath + "testingArray.json", carList);
Doesn't compile and gives the aforementioned error.
I am dumbfounded as to why, because when I run fromJson on a POJO like JSON it works. But if I run the SAME JSON data from a local file it doesn't work. It always reads it as a string even if it begins with brackets.
Path to the file is treated literally as JSON payload, so this why you see this exception. You need to create Reader based on path to the file:
try (FileReader jsonReader = new FileReader(basePath + "testingArray.json")) {
Type carList = new TypeToken<ArrayList<MyCar>>(){}.getType();
List<MyCar> myCars = gson.fromJson(jsonReader, carList);
}

Java json exception [duplicate]

This question already has an answer here:
org.json.JSONException: JSONObject["address"] is not a JSONArray
(1 answer)
Closed 5 years ago.
Using this code to call steam api. Parsing json gives me some problems. I manage to print the json in the console, accessing furhter data fails. Here is my code:
JSONObject json = new JSONObject(IOUtils.toString(new URL("http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key=XXXXXXX&steamids=XXXXXXX"), Charset.forName("UTF-8")));
System.out.println(json.get("response")); >>> (1)
int out = json.getJSONObject("players").getInt("steamid");
System.out.println(out);
Exception in thread "main" org.json.JSONException: JSONObject["players"] not found.
{
"response": {
"players": [
{
"steamid": "XXXXX",
"communityvisibilitystate": 3,
"profilestate": 1,
"personaname": "XXXXX",
"lastlogoff": 123123,
"profileurl": "http://steamcommunity.com/id/XXX/",
"avatar": "XXXXX",
"avatarmedium": "XXXX",
"avatarfull": "XXXXX",
"personastate": 1,
"primaryclanid": "XXX",
"timecreated": XXX,
"personastateflags": 0,
"gameextrainfo": "Tom Clancy's Rainbow Six Siege",
"gameid": "359550"
}
]
}
}
You just need to understand the difference between JSONObject structure and JSONArray structure
The JSONObject will starts with "{", and JSONArray starts with "[".
I just noticed your mistake, you didnt assign json.get("response") to any variable.
JSONObject json = new JSONObject(IOUtils.toString(new URL("http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key=XXXXXXX&steamids=XXXXXXX"), Charset.forName("UTF-8")));
System.out.println(json.get("response"));
JSONObject playersJson=json.get("response");
int out = playersJson.getJSONArray("players").getJSONObject(0).getInt("steamid");
So try changing your code as above.
Your problem is that players is not a JSONObject, it's a JSONArray which contains JSONObjects. In this case, players contains one JSONObject, so you need to access that object first using players[0]:
int out = json.getJSONArray("players")[0].getInt("steamid");

How do I extract the specific date out in Java? [duplicate]

This question already has answers here:
How to parse JSON in Java
(36 answers)
Closed 6 years ago.
I am currently using bufferreader to read an API documentation. Below is part of the output:
"number": 88,
"results": [
{
"time": "2013-04-15T18:05:02",
"name": "..."
},
{
"time": "2013-05-01T18:05:00",
"name": "..."
},
...
]
If I want to extract only "2013-04-15T18:05:02", which is the date. How can I do that?
You can use minimal json.
The following snippet extracts the dates and id's of all items:
JsonObject object = Json.parse(output).asObject();
JsonArray results = object.get("results").asArray();
for (JsonValue item : results) {
String date = item.asObject().getString("date", "");
String id = item.asObject().getString("id", "");
...
}
The format of your string seems to be JSON. You can use Jackson API to parse the JSON string into an array. If you don't want to use Jackson or other JSON API, you can still do it using some of the java.util.String class methods. Checkout the following sample:
List<String> dates = new ArrayList<String>();
String results = jsonString.substring(jsonString.indexOf("results"));
while((int index = results.indexOf("\\"date\\"")) != -1) {
String date = results.substring(results.indexOf(':', index), results.indexOf(',', index)).replaceAll(" ", "").replaceAll("\\"", "");
dates.add(date);
results = results.substring(results.indexOf(',', index));
}

Validating entire string as json using jackson

I'm using the following code to parse json
new com.fasterxml.jackson.databind.ObjectMapper().readTree(jsonStr)
But it parses the following string successfully since it looks like it stops processing once it finds a valid tree, even though the string in its entirety is not a valid json.
{
"name": "test",
},
"field": "c"
}
Is there a way to make it consider the entire string or stream passed? I couldn't find an appropriate option in DeserializationFeature.
Note that the solution doesn't have to involve jackson. If there's a simpler way to do that in java or scala, that'll suffice too.
With Jackson you can use Streaming API, JsonParser, to read a json like and validate like follows:
final JsonFactory jsonFactory = new JsonFactory();
jsonFactory.enable(JsonParser.Feature.STRICT_DUPLICATE_DETECTION);
try (JsonParser parser = jsonFactory.createParser(invalidJson)) {
while (!parser.isClosed()) {
parser.nextToken();
}
}
For example, if there is json string of
{
"name": "test"
},
"field": "c"
}
A JsonParseException will be thrown as follows:
Exception in thread "main"
com.fasterxml.jackson.core.JsonParseException: Unexpected character
(',' (code 44)): expected a valid value (number, String, array,
object, 'true', 'false' or 'null') at [Source: {
"name": "test"
},
"field": "c"
}; line: 3, column: 3]
jsonFactory.enable(JsonParser.Feature.STRICT_DUPLICATE_DETECTION) is to explicitly check that no duplicate JSON Object field names are encountered. If enabled, parser will check all names within context and report duplicates by throwing a JsonParseException
According to : http://jsonlint.com/
The JSON you are using is not valid. You might want to correct it to get rid of the Exception :
{
"name": "test",
"field": "c"
}

Categories