Java - JSON Parser Error - java

I am creating an application which it will send http request to a web server. The return will be in json. Here is how the json look like
[//I used a tool to make it beautiful and easy to read.
{
"item_name": "Adame",
"item_type": "Special",
"item": "Chestplate",
"item_min_lvl": "50",
"enchantment": {
"health": "0.3",
"dam": "24%",
"life": "0.1",
"xp": "24%",
"loot": "22%"
},
"def": "73"
},
{
"item_name": "Sticks'",
"item_type": "Unique",
"item": "Stick",
"item_min_lvl": "4",
"enchantment": {
"health": "0.6",
"mana": "1",
"dam": "12%",
"life": "0.3",
"xp": "17%",
"loot": "17%"
},
"min_dam": "39",
"max_dam": "34"
},
{
"item_name": "Sword'",
"item_type": "Unique",
"item": "Sword",
"item_min_lvl": "8",
"enchantment": [], //colonm 30 is [
"min_dam": "9",
"max_dam": "10"
}
]
Are you can see, the data inside the array are different. I got this error, Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 30. This is my code:
MyJSON[] data = gson.from(jsonString, MyJSON[].class);
class MyJSON {
String item_name;
String item_type;
String item;
String item_min_lvl;
Enchantment enchantment;
String min_dam;
String max_dam;
#Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("\nitem_name:").append(item_name);
builder.append("\nitem_type:").append(item_type);
builder.append("\nitem:").append(item);
builder.append("\nitem_min_lvl:").append(item_min_lvl);
builder.append("\n\nEnchantment Details:");
builder.append("\nhealth:").append(enchantment.health);
builder.append("\ndam:").append(enchantment.dam);
builder.append("\nlife:").append(enchantment.life);
builder.append("\nxp:").append(enchantment.xp);
builder.append("\nloot:").append(enchantment.loot);
return builder.toString();
}
}
class Enchantment {
String health;
String dam;
String life;
String xp;
String loot;
String mana;
}
Can anyone help me to improve my code so my code an parse the json in different case. Thanks in advanced. (P.s. that's not my web server so I can't do anything with the json)

Basically this line of JSON
"enchantment": [], //colonm 30 is [
doesn't match your POJO. You're expecting an Enchantment object, but the JSON is giving you an array. Fix your JSON to return an empty JSON object or nothing at all for the enchantment pair.
"enchantment": {}

This is a Valid JSON unless you have added comments just to show lines where is the issue?
Comments should not be part of JSON.
Here is the code that I have already shared you at you another post Java - Json deserialize data [].
You have to use ArrayList<Map<String, Object>> because the entries in the JSON string are not symmetric. You can't convert it into POJO in this case.
StringBuilder builder = new StringBuilder();
BufferedReader reader = new BufferedReader(new FileReader(new File("resources/json2.txt")));
String line = null;
while ((line = reader.readLine()) != null) {
builder.append(line);
}
reader.close();
Gson gson = new Gson();
Type listType = new TypeToken<ArrayList<Map<String, Object>>>() {
}.getType();
ArrayList<Map<String, Object>> list = gson.fromJson(builder.toString(), listType);
for (Map<String, Object> json : list) {
for (String key : json.keySet()) {
System.out.println(key + ":" + json.get(key));
}
System.out.println("===========");
}
output:
item_name:Adame
item_type:Special
item:Chestplate
item_min_lvl:50
enchantment:{health=0.3, dam=24%, life=0.1, xp=24%, loot=22%}
def:73
===========
item_name:Sticks'
item_type:Unique
item:Stick
item_min_lvl:4
enchantment:{health=0.6, mana=1, dam=12%, life=0.3, xp=17%, loot=17%}
min_dam:39
max_dam:34
===========
item_name:Sword'
item_type:Unique
item:Sword
item_min_lvl:8
enchantment:[]
min_dam:9
max_dam:10
===========
EDIT
enchantment return something like
enchantment:{health=0.6, mana=1, dam=12%, life=0.3, xp=17%, loot=17%}.
How can I get for example health?
Type mapType = new TypeToken<Map<String, String>>() {
}.getType();
String string = "{health=0.6, mana=1, dam=12%, life=0.3, xp=17%, loot=17%}";
Map<String, String> map = new Gson().fromJson(string, mapType);
for (String key : map.keySet()) {
System.out.println(key + ":" + map.get(key));
}
output:
health:0.6
mana:1
dam:12%
life:0.3
xp:17%
loot:17%

You can create a custom list type in Gson's fromJson() method to map it to a list of POJOs
Type listType = new TypeToken<ArrayList<Enhancement>>() {}.getType();
List<Enhancement> enhancements = new Gson().fromJson(jsonString, listType);
You will get a List<Enhancement>.

Related

Dynamic way to access JSON nested values in Java

I have this JSON object:
{
"maindrawer":
{
"enabled": true,
"actions":
[
{
"type": "Section",
"title": "Section 1"
},
{
"id": 1,
"type": "Primary",
"title": "Title 1",
"badge":
{
"enabled": false,
"value": 0,
"textColor": "#000000",
"badgeColor": "#ff0990"
},
"subActions":
[
{
"id": 1,
"type": "Primary",
"title": "Sub Title 1"
}
]
}
]
}
}
This is the code I'm using to access the badge -> textColor value:
public void loadJSONFromRaw(Context context, int id)
{
json = null;
try
{
//read and return json sting
InputStream is = context.getResources().openRawResource(id);
int size = is.available();
byte[] buffer = new byte[size];
is.read(buffer);
is.close();
json = new String(buffer, "UTF-8");
//convert json to object
Type type = new TypeToken<Map<String, Object>>() {}.getType();
Map<String, Object> data = new Gson().fromJson(json, type);
//access maindrawer property
Map<String, Object> maindrawer = (Map<String, Object>)data.get("maindrawer");
//access actions list
List<Object> actions = (List<Object>)maindrawer.get("actions");
//return first item in the list
Map<String, Object> action = (Map<String, Object>) actions.get(1);
//return badge object
Map<String, String> badge = (Map<String, String>) action.get("badge");
//access badge -> textColor value
String textColor = badge.get("textColor");
}
catch (IOException e)
{
e.printStackTrace();
}
}
Is there a better/faster or more dynamic way to access JSON nested properties using java/android? I'm using Gson library for this task and don't mind to switch to any other solution to make it easier as this is too much of code to write just to access a single variable.
Ideally, I'm looking for something like:
String textColor = data.get("maindrawer").get("actions").get(1).get("badge").get("textColor");
Also I'm not very interested in using POJO for now.
Lastly, I'm still new to Java so I'm probably missing something here or maybe there are some limitations? anyways thanks for you help!!
Found what I need using JsonPath library. It looks like it does similar to what I need. Here's a sample code I found:
String textColor = JsonPath.parse(json).read("$.maindrawer.actions[1].badge.textColor");
Very clean and straightforward. Hopes this will save someone else's time as well.
Since you are accessing json file locally, it means you know its structure.
So instead of using -
Map<String, Object> data = new Gson().fromJson(json, type);
You can use something like this-
Map<String, MainDrawer> data = new Gson().fromJson(json, type);
where MainDrawer is a class with member variables - enabled, actions and array of another type.
That would make easier to fetch your values like using -
mainDrawer.isEnabled()
Here are two solutions without importing a new library.
Write a simple path parser:
String textColor = (String)parse(data, "maindrawer", "actions", 1, "badge", "textColor");
//...
static Object parse(Object root, Object... params) {
Object current = root;
for (Object p : params) {
if (p instanceof Number) {
current = ((List<?>)current).get(((Number)p).intValue());
} else {
current = ((Map<?,?>)current).get(p.toString());
}
}
return current;
}
Or parse and walk through Gson's JsonElement:
JsonElement root = new Gson().fromJson(json, JsonElement.class);
String textColor = root
.getAsJsonObject().get("maindrawer")
.getAsJsonObject().get("actions")
.getAsJsonArray().get(1)
.getAsJsonObject().get("badge")
.getAsJsonObject().get("textColor")
.getAsString();
You can also do this with BSON using a single line query. You have to cast the object to the type as you go down into Nested JSON objects.
//import java.util.ArrayList;
//import org.bson.Document;
Document root = Document.parse("{ \"maindrawer\" : { \"enabled\" : true, \"actions\" : [{ \"type\" : \"Section\", \"title\" : \"Section 1\" }, { \"id\" : 1, \"type\" : \"Primary\", \"title\" : \"Title 1\", \"badge\" : { \"enabled\" : false, \"value\" : 0, \"textColor\" : \"#000000\", \"badgeColor\" : \"#ff0990\" }, \"subActions\" : [{ \"id\" : 1, \"type\" : \"Primary\", \"title\" : \"Sub Title 1\" }] }] } }");
System.out.println(((String)((Document)((Document)((ArrayList)((Document)root.get("maindrawer")).get("actions")).get(1)).get("badge")).get("textColor")));

Getting the values from a JSON string in Java using GSON

I hope someone can show me where i'm doing it wrong...
I'm using sendgrid for my email tracking and it is posting a JSON like the following:
[
{
"email": "john.doe#sendgrid.com",
"timestamp": 1337966815,
"event": "click",
"url": "http://sendgrid.com"
"userid": "1123",
"template": "welcome"
}
]
Now i want to get the value of for example for "timestamp" which is 1337966815 . I've tried the following:
StringBuffer jb = new StringBuffer();
String line = null;
try {
BufferedReader reader = req.getReader();
while ((line = reader.readLine()) != null)
jb.append(line);
} catch (Exception e) { /*report an error*/ }
String jsonString = jb.toString();
Gson gson = new Gson();
JsonObject jsonObject = gson.fromJson(jsonString, JsonObject.class);
String timeStam = jsonObject.get(timestamp).toString();
The string of jsonString gives me the following which i think is in the right format:
[ { "email": "john.doe#sendgrid.com", "timestamp": 1337966815, "event": "click", "url": "http://sendgrid.com" "userid": "1123", "template": "welcome" }]
But i'm getting the following error at this line of code - JsonObject jsonObject = gson.fromJson(jsonString, JsonObject.class);
java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 52
What am I doing wrong? Is it the format of jsonString that is confusing the JsonObject?
Any help would be very much appreciated.
Kind regards
Francois
The JSON you show in both examples is invalid. There is a comma missing after "url":"http://sendgrid.com"
Ignoring that, the JSON you show is an array of JSON objects, not an object. This is what the [] denotes (correcting the missing comma):
[
{
"email": "john.doe#sendgrid.com",
"timestamp": 1337966815,
"event": "click",
"url": "http://sendgrid.com",
"userid": "1123",
"template": "welcome"
}
]
If you are not mapping this JSON to a Java POJO, then you would want to use Gson's JsonParser to parse your String to a JsonElement (Note you could even use it to parse directly from the Stream, but this if for how you have your code now).
JsonElement je = new JsonParser().parse(jsonString);
Now you have what's called a "parse tree". This JsonElement is the root. To access it as an array you're going to do:
JsonArray myArray = je.getAsJsonArray();
You only show this array containing one object, but let's say it could have more than one. By iterating through the array you can do:
for (JsonElement e : myArray)
{
// Access the element as a JsonObject
JsonObject jo = e.getAsJsonObject();
// Get the `timestamp` element from the object
// since it's a number, we get it as a JsonPrimitive
JsonPrimitive tsPrimitive = jo.getAsJsonPrimitive("timestamp");
// get the primitive as a Java long
long timestamp = tsPrimitive.getAsLong();
System.out.println("Timestamp: " + timestamp);
}
Realize that Gson primarily is meant for Object Relational Mapping where you want to take that JSON and have it converted to a Java object. This is actually a lot simpler:
public class ResponseObject {
public String email;
public long timestamp;
public String event;
public String url;
public String userid;
public String template;
}
Because you have array of these, you want to use a TypeToken and Type to indicate your JSON is a List of these ResponseObject objects:
Type myListType = new TypeToken<List<ResponseObject>>(){}.getType();
List<ResponseObject> myList = new Gson().fromJson(jsonString, myListType);

Convert complex Java object to Json using Gson

I am using GSON to serialize Java object.
I have a Java class with following properties.
String property1;
Map<String, HashMap> property2 = new HashMap<>();
Map<String, ArrayList<String>> property3 = new HashMap<>();
Map<String, String[]> property4 = new HashMap<>();
I want to convert this to Json. Because of the maps with HashMaps inside, it has become difficult. I know I can get Json of a map with gsonObject.toJson(map). But I want all these properties in the Json Object. (all in one. not Concatenating many objects)
Can anyone help me to get this done?
I don't see what the problem is. Gson can serialize Maps just fine.
Assuming your class is named Test
Test test = new Test();
test.property1 = "some value";
HashMap<String, Integer> map = new HashMap<>();
map.put("one", 1);
map.put("fourty two", 42);
test.property2.put("property2-key", map);
ArrayList<String> strings = new ArrayList<>(Arrays.asList("string1",
"string2", "string3"));
test.property3.put("property3-key", strings);
String[] stringArray = { "array1", "array2", "array3" };
test.property4.put("property4-key", stringArray);
Gson gson = new Gson();
String json = gson.toJson(test);
System.out.println(json);
It generates the following
{
"property1": "some value",
"property2": {
"property2-key": {
"fourty two": 42,
"one": 1
}
},
"property3": {
"property3-key": [
"string1",
"string2",
"string3"
]
},
"property4": {
"property4-key": [
"array1",
"array2",
"array3"
]
}
}

Using GSON to parse a JSON array

I have a JSON file like this:
[
{
"number": "3",
"title": "hello_world",
}, {
"number": "2",
"title": "hello_world",
}
]
Before when files had a root element I would use:
Wrapper w = gson.fromJson(JSONSTRING, Wrapper.class);
code but I can't think how to code the Wrapper class as the root element is an array.
I have tried using:
Wrapper[] wrapper = gson.fromJson(jsonLine, Wrapper[].class);
with:
public class Wrapper{
String number;
String title;
}
But haven't had any luck. How else can I read this using this method?
P.S I have got this to work using:
JsonArray entries = (JsonArray) new JsonParser().parse(jsonLine);
String title = ((JsonObject)entries.get(0)).get("title");
But I would prefer to know how to do it (if possible) with both methods.
Problem is caused by comma at the end of (in your case each) JSON object placed in the array:
{
"number": "...",
"title": ".." , //<- see that comma?
}
If you remove them your data will become
[
{
"number": "3",
"title": "hello_world"
}, {
"number": "2",
"title": "hello_world"
}
]
and
Wrapper[] data = gson.fromJson(jElement, Wrapper[].class);
should work fine.
Gson gson = new Gson();
Wrapper[] arr = gson.fromJson(str, Wrapper[].class);
class Wrapper{
int number;
String title;
}
Seems to work fine. But there is an extra , Comma in your string.
[
{
"number" : "3",
"title" : "hello_world"
},
{
"number" : "2",
"title" : "hello_world"
}
]
public static <T> List<T> toList(String json, Class<T> clazz) {
if (null == json) {
return null;
}
Gson gson = new Gson();
return gson.fromJson(json, new TypeToken<T>(){}.getType());
}
sample call:
List<Specifications> objects = GsonUtils.toList(products, Specifications.class);
Wrapper[] data = gson.fromJson(jElement, Wrapper[].class);

gson: deserialize json with composite keys

I would like to use this kind of Objects with json:
class Message{
int code;
String user;
Map<List<String>, List<String>> profile;
}
it seems json can't handle Object keys as array, so I would need to tranfer them like that:
{
"code": 1,
"user": "John",
"profile": {
"type,1": ["tester"],
"lang,2": ["fr", "it", "en", "sp"],
"rate,4": ["10", "1000"],
"date,5": ["134118329", "1341973211"]
}
}
or
{
"code": 1,
"user": "John",
"profile": {
"type": [1,"tester"],
"lang": [2,"fr", "it", "en", "sp"],
"rate": [4,"10", "1000"],
"date": [5,"134118329", "1341973211"]
}
}
the first json is probably simpler, even if it relies on a hard string separator,
So with the first one it seems I have to write this huge adapter:
private static class MyAdapter implements JsonSerializer<Map<List<String>, List<String>>>,
JsonDeserializer<Map<List<String>, List<String>>> {
#Override
public JsonElement serialize(Map<List<String>, List<String>> m,
Type type, JsonSerializationContext context) {
JsonObject j = new JsonObject();
for (Entry<List<String>, List<String>> e : m.entrySet() ){
JsonArray jj=new JsonArray();
for (String s : e.getValue()){
jj.add(new JsonPrimitive(s));
}
j.add(e.getKey().get(0)+","+e.getKey().get(1), jj);
}
return j;
}
#Override
public Map<List<String>, List<String>> deserialize(JsonElement json, Type type,
JsonDeserializationContext arg2) throws JsonParseException {
Map<List<String>, List<String>> m = new HashMap<List<String>, List<String>>();
JsonObject jObject = json.getAsJsonObject();
for (Entry<String, JsonElement> e : jObject.entrySet() ){
List<String> key = new ArrayList<String>();
List<String> value = new ArrayList<String>();
for (String s : e.getKey().split(",") ){
key.add(s);
}
for (JsonElement jj : e.getValue().getAsJsonArray() ){
value.add(jj.getAsString());
}
m.put(key, value);
}
return m;
}
}
...
GsonBuilder g = new GsonBuilder();
g.registerTypeAdapter(Map.class, new MyAdapter());
Gson gson = g.create();
Is there faster ways? I guess yes, the idea is just to split the key into a Map composite key, because each part of the key has an important meaning
thx, and sry for the edit
This will not work.
Object declaration syntax according specification:
An object is an unordered set of name/value pairs. An object begins
with { (left brace) and ends with } (right brace). Each name is
followed by : (colon) and the name/value pairs are separated by ,
(comma).
This:
{
["type", 1]: "tester",
["lang", 2]: ["fr", "it", "en", "sp"],
["rate", 4]: ["10", "1000"],
["date", 5]: ["134118329", "1341973211"]
}
is invalid, and therefore you can't read with Gson causing the following exception: com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING
the name in front of json: aRequest = is invalid too.
For profile values, I don't know if there is better than Object unfortunately
It looks like a List<String> or String[] would be a natural fit, with special handling for the single value entry that is not in a list format.
Unfortunately, the issue of deserializing a JSON structure that is sometimes a list and sometimes an object has come up repeatedly on SO. Fortunately, so have solutions.

Categories