I have a string object representing a json object returning for a network task. I need to convert it into a Map (or HashMap). I've been using gson, but it has been unsuccessful. Here is the json string (please excuse indentation, for I had to manually add newline spaces):
{
"plans":{
"Ankle Recovery":{
"StartDate":"09/24/2018",
"Progress":0.6666666666666666,
"Tasks":[
{
"date":"10/16/2018",
"amount":200,
"task":"ice ankle for 30 min",
"completed":true,
"requirementType":"steps"},
{
"date":"10/17/2018",
"amount":200,
"task":"ice ankle for 30 min",
"completed":true,
"requirementType":"steps"
},
{
"date":"10/18/2018",
"amount":200,
"task":"ice ankle for 30 min",
"completed":false,
"requirementType":"steps"
}
],
"Username":"[email protected]",
"Doctor":"Mike Michaels",
"EndDate":"12/24/2018"}},
"status":true
}
This is the code I've been using to make the transformation:
private Map<String, String> plans;
plans = new Gson().fromJson(result, new TypeToken<Map<String, String>>() {}.getType());
Neither nor has worked. I've tried some different solutions across Stack Overflow, but none yield success to this point.
I'm also getting an exception thrown that I don't quite understand:
com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected a string but was BEGIN_OBJECT at line 1 column 11
(Column 11 is just before the first quote in "AnkleRecovery")
I'd like to use simple gson to make this work if possible. But I'm open to alternative solutions.
2 Answers 2
The JSON you posted is not valid, line 3:
"Ankle Recovery" : {
// / \
// this is what you are missing
This tool will help you verify the JSON structure and format it as well: https://jsonlint.com/
Now to the actual problem. Your JSON has a following structure:
{
"plans": Object,
"status": Boolean,
}
Neither of these are strings ( object != string, boolean != string ).
Such a structure can not be mapped to Map<String, String>
as this requires the value to be a string.
You will need to create multiple POJOs to define your structure and then map to these, e.g.:
class Project {
public Map<String,Plan> plans;
public Boolean status;
}
class Plan {
public String StartDate;
public Double Progress;
public List<Task> tasks;
...
}
class Task {
...
}
4 Comments
Map<String, Object>
, but that would take more introspection to decode the results ... just saying ;)Ankle Recovery
as a key - which is, frankly, an abdominal name for a key - and might signify that the key is not stableAnkle Recovery
is not a stable key as it looks like a plan name, thus the Map<String, Plan>
structure, where String is the plan name.Disclaimer...
I would always investigate using one or more POJOs which can be used to represent the data structure if at all possible.
Without more information, it's impossible to know if keys like Ankle Recovery
are stable or not, or if they might change.
"A" possible solution
Generally, JSON is in the form of key/value pairs, where the value might be another JSON object, array or list of other values, so you "could" process the structure directly, for example...
String text = "{\n"
+ " \"plans\":{\n"
+ " \"Ankle Recovery\":{\n"
+ " \"StartDate\":\"09/24/2018\",\n"
+ " \"Progress\":0.6666666666666666,\n"
+ " \"Tasks\":[\n"
+ " {\n"
+ " \"date\":\"10/16/2018\",\n"
+ " \"amount\":200,\n"
+ " \"task\":\"ice ankle for 30 min\",\n"
+ " \"completed\":true,\n"
+ " \"requirementType\":\"steps\"\n"
+ " },\n"
+ " {\n"
+ " \"date\":\"10/17/2018\",\n"
+ " \"amount\":200,\n"
+ " \"task\":\"ice ankle for 30 min\",\n"
+ " \"completed\":true,\n"
+ " \"requirementType\":\"steps\"\n"
+ " },\n"
+ " {\n"
+ " \"date\":\"10/18/2018\",\n"
+ " \"amount\":200,\n"
+ " \"task\":\"ice ankle for 30 min\",\n"
+ " \"completed\":false,\n"
+ " \"requirementType\":\"steps\"\n"
+ " }\n"
+ " ],\n"
+ " \"Username\":\"[email protected]\",\n"
+ " \"Doctor\":\"Mike Michaels\",\n"
+ " \"EndDate\":\"12/24/2018\"\n"
+ " }\n"
+ " },\n"
+ " \"status\":true\n"
+ "}";
Gson gson = new Gson();
Map<String, Object> fromJson = gson.fromJson(text, Map.class);
Map<String, Object> plans = (Map<String, Object>) fromJson.get("plans");
Map<String, Object> recovery = (Map<String, Object>) plans.get("Ankle Recovery");
List<Map<String, Object>> tasks = (List<Map<String, Object>>) recovery.get("Tasks");
for (Map<String, Object> taks : tasks) {
for (Map.Entry<String, Object> entry : taks.entrySet()) {
System.out.println(entry.getKey() + " = " + entry.getValue());
}
}
Now, this would get you the output of ...
date = 10/16/2018
amount = 200.0
task = ice ankle for 30 min
completed = true
requirementType = steps
date = 10/17/2018
amount = 200.0
task = ice ankle for 30 min
completed = true
requirementType = steps
date = 10/18/2018
amount = 200.0
task = ice ankle for 30 min
completed = false
requirementType = steps
Having said all that, your own parsing might be a lot more involved, have to inspect if certain keys exist or not and taking appropriate action as required
Map<String, String>
, it has a list of values as well. Personally, I'd devise a POJO which mirrored the structure, but that's me. May tryMap<String, Object>
instead?"Ankle Recovery"{
is wrong - once I correct for that, I can parse it toMap<String, Object>
"Ankle Recovery"{
needs to be"Ankle Recovery": {
- one of the first things I always do is put the text through a JSON formatter/validator ;)