I've searched the web for a solution, but, the answers I found only help when the JSON file being parsed is solely a map.
I was wondering if there is a way to parse an object that has an HashMap along with other data.
For example:
public class Data
{
String aName;
HashMap<String, Object> objects;
List<String> aExpressions;
}
I can parse the name and expressions but I don't know how to parse the entire JSON object as a whole.
I think it has something to do with type tokens, but that only works if the whole object is a HashMap.
The presence of HashMap in your class does not change how Gson converts it to and from json.
data d = new data();
//set some values
String json = new Gson().toJson(d);
data d2 = new Gson().fromJson( json, data.class);
That's pretty much it.
Since
JSON can represent four primitive types (strings, numbers,
booleans, and null) and two structured types (objects and arrays).
A string is a sequence of zero or more Unicode characters
[UNICODE].
An object is an unordered collection of zero or more name/value
pairs, where a name is a string and a value is a string, number,
boolean, null, object, or array.
An array is an ordered sequence of zero or more values.
Gson acts 2 ways:
you can let Gson deserialize creating a combination of maps, lists and primitives or
you can specify exactly the kind of object you want, and through reflection, it will fill the desidered fields.
You can mix these two approaches and of course you can do a lot more to solve all your parsing cases (like using custom type adapters).
I prepared you a little example using your class that shows how Gson can parse your data using a combination of maps/list/primitives or passing your class (that has a mixed approach).
package stackoverflow.questions;
import java.util.*;
import com.google.gson.Gson;
public class Q20154323 {
public static class Data {
public String aName;
public HashMap<String, Object> objects;
public List<String> aExpressions;
#Override
public String toString() {
return "Data [aName=" + aName + ", objects=" + objects + ", aExpressions=" + aExpressions + "]";
}
}
public static void main(String[] args) {
Data d = new Data();
d.aName = "Test";
d.objects = new HashMap<>();
d.aExpressions = new ArrayList<>();
d.objects.put("key1", 1L);
d.objects.put("key2", new Date());
d.aExpressions.add("Stack");
d.aExpressions.add("Overflow");
Gson g = new Gson();
String json = g.toJson(d);
System.out.println("As JSON: " +json);
Data d2 = g.fromJson(json, Data.class);
System.out.println("As \"casted\" data type: " + d2);
Object o3 = g.fromJson(json, Object.class);
System.out.println("As \"free\" object: " + o3);
}
}
and this is the execution. It shows you two way of parsing the JSON string that I created using your initial class.
As JSON: {"aName":"Test","objects":{"key2":"Nov 23, 2013 1:33:23 AM","key1":1},"aExpressions":["Stack","Overflow"]}
As "casted" data type: Data [aName=Test, objects={key2=Nov 23, 2013 1:33:23 AM, key1=1.0}, aExpressions=[Stack, Overflow]]
As "free" object: {aName=Test, objects={key2=Nov 23, 2013 1:33:23 AM, key1=1.0}, aExpressions=[Stack, Overflow]}
You can use one or other approach as your needs.
About the TypeToken, due to generics erasure, something like this
List<Data> list = new Gson().parse(aJsonString, List<Data>.class)
won't work, you have to do something like
Type listType = new TypeToken<List<Data>>() {}.getType();
List<Data> list = new Gson().parse(aJsonString, listType.class)
but this case applies, pratically, when your JSON is an array and you want to deserialize it into a list of custom classes.
Related
I'm try to convert JsonObject to String by using GSON library. But the result output will have one more layer of parent "map" wrap up the json. Please let me know any wrong i did why the layer of parent "Map" will appear?
i do even try to covert the bean by using new Gson().toJson(bean); but the output result also have one more layer of parent "map" wrap up the json.
Condition i need to fulfil by use
1) Mutable object
2) GSON
3) method might handle other object Type
Maven project using as bellow library:
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.5</version>
</dependency>
Java code bellow(example for understand only not the real code, will using in T):
List<JSONObject> records = new ArrayList <JSONObject> ();
JSONObject bean = new JSONObject();
bean.put("A", "is A");
bean.put("B", "is B lah");
bean.put("C", "is C lah");
bean.put("D", "is D");
records.add(bean);
String JSONBody2 = new Gson().toJson(records);
I expect the output is
[{"D":"is D","A":"is A","B":"is B lah","C":"is C lah"}]
but the actual output is
[{"map":{"D":"is D","A":"is A","B":"is B lah","C":"is C lah"}}]
Actual code is as below
public String Json( String json, List<T> list) {
String JSONBody = new Gson().toJson(list);
}
I need to Serialization by using gson that's why i put the T. but i don't have idea why the "map" is appeared here. as previously it work without Parent "Map" wrap up. (same code and same library just new recreated project but having this issue)
try
String JSONBody2 = record.toString());
will give you [{"A":"is A","B":"is B lah","C":"is C lah","D":"is D"}]
You can get more better understanding of type conversion from this link https://stackoverflow.com/a/27893392/4500099
Although others have already answered it, i would like to highlight one important learning. There are 2 ways to convert JsonObject to string.
One is new Gson().toJson(..) and other is JsonObject.toString().
Although both will produce same results in many cases but if the JsonObject has some string fields containing valid urls or base64 ecoded values, the first method will convert & and = into corresponding utf-8 representative characters which will make your urls and base64 ecoded values corrupted. However, second method keep those values intact.
Use JSONArray instead of List , it will give you the desired output:
JSONArray records = new JSONArray();
JSONObject bean = new JSONObject();
bean.put("A", "is A");
bean.put("B", "is B lah");
bean.put("C", "is C lah");
bean.put("D", "is D");
records.put(bean);
String JSONBody2 = new Gson().toJson(records);
Don't use a JSON Object. Just use Object
List<Object> records = new ArrayList<>();
Map<String, String> bean = new HashMap<>();
bean.put("A", "is A");
bean.put("B", "is B lah");
bean.put("C", "is C lah");
bean.put("D", "is D");
records.add(bean);
String JSONBody2 = new Gson().toJson(records);
System.out.println(JSONBody2);
Output is
[{"A":"is A","B":"is B lah","C":"is C lah","D":"is D"}]
If you look at the implementation of org.json.JSONObject, it internally contains a variable called map where it stores all the data. This is the constructor
public JSONObject() {
this.map = new HashMap<String, Object>();
}
When GSON tries to convert to JSON, it just recursively looks into the object and converts all variables to JSON. So in this case, it converts the internal map, which shows up in the Json output.
I need to work with Jsons containing loads of special characters and spaces. I've read somewhere else here on stack that the Gson library (for Java) does a very good job so I'm using that. While using the fromJson(Json string, object class) method to turn a Json string into an object, I noticed that as soon as the data in the string contains any special character or white space, an exception is thrown (Unterminated object). Removing the special characters makes it work as intended.
To better exemplify the situation:
ArrayList<Person> people = new ArrayList<Person>();
Person p1 = new Person("Matteo", 999);
Person p2 = new Person("Adam", 999);
Person p3 = new Person("Steve", 999);
people.add(p1);
people.add(p2);
people.add(p3);
String json = new Gson().toJson(people);
System.out.println(json);
ArrayList people2 = new Gson().fromJson(json, ArrayList.class);
for (int i = 0; i < people2.size(); i++) {
Person pn = new Gson().fromJson(people2.get(i).toString(), Person.class);
System.out.println(people2.get(i).toString());
System.out.println(pn.name);
}
class Person {
String name;
int age;
Person(String cname, int cage) {
name = cname;
age = cage;
}
}
The code above works as intended, BUT if instead of Matteo I type Ma:tteo or Ma tteo or any other string that contains special characters then it breaks.
So is there a way to circumvent this issue? Alternatively is there a better solution than using this library?
Thanks in advance
Gson does its work fine.
There are two problems with how you are using the Gson library:
You are using fromJson(String, Class<T>) in conjunction with generics. The documentation states that when using generics, you should use fromJson(String, Type):
For the cases when the object is of generic type, invoke fromJson(String, Type).
You are first deserializing the JSON string into a Java structure – in your case an ArrayList – and then you are looping over it, and for each object, you are deserializing it again, relying on the toString() method of the object contained in the ArrayList. As a matter of fact, your list does not contain Person objects at all, instead, it contains LinkedTreeMap objects from the com.google.gson.internal package. You can get the object's class by calling people2.get(i).getClass().
You really don't need to walk over the elements of this ArrayList and deserialize the elements yourself. If it was required to walk over each list contained in your structure, it would be a world of hurt when that structure was more complex than yours.
Simply get rid of your for-loop and replace your toJson(String, Class<T>) call with a call to fromJson(String, Type). That's all.
// We're using the TypeToken class, to handle generic types
Type type = new TypeToken<List<Person>>() { }.getType();
List<Person> newPeople = new Gson().fromJson(json, type);
// Print all names
newPeople.stream()
.map(t -> t.name)
.forEach(System.out::println);
Notes
A part of the source of the problem is that you are using a raw type (ArrayList). Don't use raw types, they are only allowed for backward compatibility.
I need to work with Jsons containing loads of special characters and spaces.
It's still not clear what you mean with special characters. The JSON standard clearly defines what is allowed and what is not. Your JSON string should follow the standards, and then you're good to go.
Try with this code.
ArrayList<Person> people = new ArrayList<Person>();
Person p1 = new Person("Ma:tteo", 999);
Person p2 = new Person("Adam", 999);
Person p3 = new Person("Steve", 999);
people.add(p1);
people.add(p2);
people.add(p3);
Gson gson = new Gson();
String json = gson.toJson(people);
System.out.println(json);
List<Person> personList = gson.fromJson(json, new TypeToken<List<Person>>(){}.getType());
for (int i = 0; i < personList.size(); i++) {
Person person = personList.get(i);
System.out.println(person.toString());
System.out.println(person.name);
}
I want make json
records":[ {"MON_PRIORITY":"","MON_ICR_ACCNO":"100000010010","MON_REPORT_DATE":"","MON_STATUS":"",
But my json is
{"MON_PRIORITY":"","MON_ICR_ACCNO":"100000010010","MON_REPORT_DATE":"","MON_STATUS":"",
My jsp code is
HashMap jsonRecordval = (HashMap) hshValues.get("jsonRecord");
String json="";
json = new Gson().toJson(jsonRecordval );
Thanks..
What you're getting is the JSON produced by a Hashmap. e.g. {"key":"value"}. Breaking it down piece by what, your desired json is a representation of an object { with a records field "records" that contains an array [ of the contents of your hashmap {"key":"value"}
To do that, it's easiest to create an object with instance variables corresponding to the fields to expected output. Something like
public class JsonRecords {
private final List<HashMap> records = new ArrayList<>;
public JsonRecords(HashMap recordsVal) {
records.add(recordsVal);
}
//Getters and setters
}
Then use it to build your JSON
HashMap jsonRecordval = (HashMap) hshValues.get("jsonRecord");
String json = new Gson().toJson(new JsonRecords(jsonRecordval));
the problem is to fill Java object fields both of java standart types(Long, Boolean, etc.) and collections of those from a Map<String, List<String>>.
Inspecting target object's fields via reflecton one can see which field is a Collection and which is not. In second case if Map element has a single value one can use something like BeanUtils to set such fields:
BeanUtils.setProperty(bean, name, stringList.get(0));
But in first case I don't have idea how to determine a type of collection(except of sequentially checking if it is List, Set, Map) and type of it's elements. Simple
BeanUtils.setProperty(bean, name, stringList);
will be valid only if target field type is a List<String>.
But the Jackson library succesfully solves the same problem. For example, consider class:
public static class TestObject {
private int integerField;
private String stringField;
private List<String> stringList;
private List<Integer> intList;
...getters...setters...
}
Now it can be filled with content using Jackson library:
ObjectMapper mapper = new ObjectMapper();
TestObject obj = mapper.readValue("{" +
"\"stringField\": \"stringValue\", " +
"\"integerField\": 42, " +
"\"stringList\":[\"1\", \"2\", \"3\"]," +
"\"intList\":[3, 2, 1]" +
"}", TestObject.class);
Works perfect. So my question: is there way to use a library such Jackson for solving my problem?
Thanks.
I need to create constant json string or a json sorted on keys. What do I mean by constant json string? Please look into following code sample, which I created.
My Code 1:
public class GsonTest
{
class DataObject {
private int data1 = 100;
private String data2 = "hello";
}
public static void main(String[] args)
{
GsonTest obj=new GsonTest();
DataObject obj2 = obj.new DataObject();
Gson gson = new Gson();
String json = gson.toJson(obj2);
System.out.println(json);
}
}
Output 1:
{"data1":100,"data2":"hello"}
My Code 2:
public class GsonTest
{
class DataObject {
private String data2 = "hello";
private int data1 = 100;
}
public static void main(String[] args)
{
GsonTest obj=new GsonTest();
DataObject obj2 = obj.new DataObject();
Gson gson = new Gson();
String json = gson.toJson(obj2);
System.out.println(json);
}
}
Output 2:
{"data2":"hello","data1":100}
If you see, if I switch variables (data1 & data2 in DataObject class), I get different json. My objective to get same json, even if somebody changes position of the class variables. I get it when somebody adds new variables, json would change. But json shouldn't change when variables are moved around. So, my objective is to get standard json, possibly in sorted keys order for same class. If there is nested json, then it should be sorted in the nested structure.
Expected output on run of both the codes:
{"data1":100,"data2":"hello"} //sorted on keys!! Here keys are data1 & data2
I understand, I need to change something in String json = gson.toJson(obj2); line, but what do I have to do?
Why I need them to be order?
I need to encode the json string and then pass it to another function. If I change the order of keys, even though value remain intact, the encoded value will change. I want to avoid that.
First of all, the keys of a json object are unordered by definition, see http://json.org/.
If you merely want a json string with ordered keys, you can try deserializing your json into a sorted map, and then serialize the map in order to get the sorted-by-key json string.
GsonTest obj=new GsonTest();
DataObject obj2 = new DataObject();
Gson gson = new Gson();
String json = gson.toJson(obj2);
TreeMap<String, Object> map = gson.fromJson(json, TreeMap.class);
String sortedJson = gson.toJson(map);
Like others have mentioned that by design JSON is not supposed to have sorted keys in itself. You can also come up with a recursive solution to do it. I won't say my solution is very efficient but it does the intended job. Please have a look at the following piece of code.
private static JsonObject sortAndGet(JsonObject jsonObject) {
List<String> keySet = jsonObject.keySet().stream().sorted().collect(Collectors.toList());
JsonObject temp = new JsonObject();
for (String key : keySet) {
JsonElement ele = jsonObject.get(key);
if (ele.isJsonObject()) {
ele = sortAndGet(ele.getAsJsonObject());
temp.add(key, ele);
} else if (ele.isJsonArray()) {
temp.add(key, ele.getAsJsonArray());
} else
temp.add(key, ele.getAsJsonPrimitive());
}
return temp;
}
Input:
{"c":"dhoni","a":"mahendra","b":"singh","d":{"c":"tendulkar","b":"ramesh","a":"sachin"}}
Output:
{"a":"mahendra","b":"singh","c":"dhoni","d":{"a":"sachin","b":"ramesh","c":"tendulkar"}}
Perhaps a work around is for your class wrap a TreeMap which maintains sort order of the keys. You can add getters and setters for convenience. When you gson the TreeMap, you'll get ordered keys.