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.
Related
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 have a case where I need to merge multiple JSONs objects into one JSON.
A single response looks like this:
{"name":"MyName"}
Multiple merged JSON looks like this:
["{\"name\":\"name\"}","{\"name\":\"MyName\"}"]
The problem here is that the child JSONs that I want to include can come either from a Java object or are available as String itself.
MyRequest request = new MyRequest();
request.setName("name");
String singleJson = new Gson().toJson(request);
String fromSomePlaceElse = "{\"name\":\"MyName\"}";;
List<String> list = Lists.newArrayList(singleJson,fromSomePlaceElse);
System.out.println(new Gson().toJson(list));
The above gives me the following output:
["{\"name\":\"name\"}","{\"name\":\"MyName\"}"]
instead of:
[{"name":"MyName"}, {"name":"MyName"}]
I don't want to parse the already existing JSON and do the following:
List<MyRequest> list2 = Lists.newArrayList(request, new Gson().fromJson(fromSomePlaceElse, MyRequest.class));
System.out.println(new Gson().toJson(list2));
Can this be done using Gson ?
Just print it.
List<String> list = Lists.newArrayList(singleJson,fromSomePlaceElse);
System.out.println(list);
Then you can get
[{"name":"name"}, {"name":"MyName"}]
if you want json in form of string,you can directly use ---
new Gson().toJson(yourList, new TypeToken<List<JsonObject>>(){}.getType())
Try the given library to merge any amount of JSON-objects at once. The result can be returned as String or JsonObject (GSON). Please reference the code in this answer.
I have a json string which looks something like this :
{"employees":[
{"firstName":"John", "lastName":"Doe"},
{"firstName":"Anna", "lastName":"Smith"},
{"firstName":"Peter", "lastName":"Jones"}
]}
When I use gson parser to convert that to a Map<String,String> map. The gson converts the type to Map<String,ArrayList>. And when I try to print the class name using System.out.println(map.get("employees").getClass().toString()). But this throws an exception - java.lang.ClassCastException: java.util.ArrayList cannot be cast to java.lang.String.
my code looks something like this
String npv = json_string_provided_above;
Map<String,String> mp = new HashMap<String, String>();
mp = new Gson().fromJson(npv,mp.getClass());
System.out.println(mp.get("employees").getClass().toString());
The json is given as input by user in string format(and it will always be a valid json string). And one thing is sure that I can't assume anything about the type of data. Because it will be provided by user.
So now actually I want that even If the user inputs something like arrays of string or arrays of objects. It should not convert the them to List,
Is there is a way I can hard code the type and keep them in Map<String,String>. so in the above example when I do map.get("employees") it should give me the [{"firstName":"John", "lastName":"Doe"},{"firstName":"Anna", "lastName":"Smith"},{"firstName":"Peter", "lastName":"Jones"}] as string not list. I am not sure how to do this.
Thank you for your help,
Jon is right, just use Map<String, Object>
String npv = json_string_provided_above;
Map<String,Object> mp = new HashMap<String, Object>();
mp = new Gson().fromJson(npv, mp.getClass());
System.out.println(mp.get("employees").toString());
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.
I've got a JSON string that I want to convert to a Map structure where Object is either a Java version of a basic type (i.e. String, Int, Double), a Map. or a List.
The sample string I'm using for my tests is:
"{\"cases\":[{\"documents\":[{\"files\":[{\"name\":\"a.pdf\"}]}]}]}"
This should read as an array of cases that each have an array of documents, that each have an array of files, that each have a name
I've tried Google's Gson, but
Gson gson = new Gson();
List<Map<String, Object>> results = gson.fromJson(dictString, List.class);
gives me:
com.google.gson.JsonParseException: The JsonDeserializer com.google.gson.DefaultTypeAdapters$CollectionTypeAdapter#561777b1 failed to deserialize json object {"cases":[{"documents":[{"files":[{"name":"a.pdf"}]}]}]} given the type interface java.util.List
and I tried Jackson, but
List<Map<String, Object>> results = (List<Map<String, Object>>) new ObjectMapper().readValue(dictString, List.class);
gave me:
org.codehaus.jackson.map.JsonMappingException: Can not deserialize instance of java.util.List out of START_OBJECT token
at [Source: java.io.StringReader#1c5aebd9; line: 1, column: 1]
Do you have any suggestions? Either for how to use either of the above correctly, or for another parser that gives me what I want?
Cheers
Nik
I stumbled in here with the same problem and I found a simple solution. I'm posting a more clear answer just in case it helps someone else:
String jsonString = "{ \"first-name\" : \"John\" }";
//creates, essentially a wrapper for a HashMap containing your JSON dictionary
JSONObject genericMap = new JSONObject(jsonString);
//calls to "put" and "get" are delegated to an internal hashmap
String name = (String) genericMap.get("first-name");
assertEquals("John", name); //passes
genericMap.put("last-name", "Doe"); //add any object to the hashmap
//the put methods can be used fluidly
genericMap.put("weight", 150).put("height", "5'10").put("age", "32");
//finally, you can write it back out to JSON, easily
String newJson = genericMap.toString(4); //pretty-print with 4 spaces per tab
log.debug(newJson);
this prints the following:
{
"age": "32",
"first-name": "John",
"height": "5'10",
"last-name": "Doe",
"weight": 150
}
Add this dependency to your project like this:
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20090211</version>
</dependency>
Or download the JAR directly:
http://repo1.maven.org/maven2/org/json/json/20090211/json-20090211.jar
I already had this class available (it was a transient dependency of something else in our project). So be sure to check if it's already there first. You might get lucky, too.
Given your description, it sounds like your definition doesn't match up. It dounds like it should be something like: List<List<list<String>>>
It's a bit more manual but have a look here:
http://json.org/java/
This will give you a JSONObject that is much easier to use than parsing the string yourself, but you will still have to drill into it and manually build your map. Kind of a half and half solution.
The easiest thing might be just to do it yourself: use something like GSON or tapestry's JSONObject to construct a java representatin of your json, then just iterate through it and build whatever map structure you like.
Using gson library:
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>${gson.version}</version>
</dependency>
First you need to create a type. Let's suppose you need a Map<String,Foo> then change:
private static final Type INPUT_MAP_TYPE = new TypeToken<Map<String, Foo>>() {}.getType();
Then, have a generic method of the type:
protected <T> T readJson(String fileName, Type type) {
InputStreamReader ir = new InputStreamReader(getClass().getClassLoader().getResourceAsStream(fileName));
return new Gson().fromJson(ir, type);
}
Where Type is in package java.lang.reflect;
Enjoy:
Map<String, Foo> inputParams = readJson("path/to/my.json", INPUT_MAP_TYPE);