org.JSONObject vs Map in Gson serialization [duplicate] - java

I have a POJO class as:
public class D{
private JSONObject profileData;
public JSONObject getProfileData ()
{
return profileData;
}
public void setProfileData (JSONObject profileData)
{
this.profileData = profileData;
}
}
Now I populate this class like:
for (int i =0; i<identities.size();i++){
D d = new D();
d.setProfileData(profileData);
dList.add(d);
}
I create JSON object for profileData from GSON using a HashMap:
profileDataInJson = new JSONObject(gson.toJson(map1));
Where the signature of profileDataInJson is: JSONObject profileDataInJson = null;
Now the resultant JSON is like:
"profileData":{"map":{"ioCinema":"firstValue","ioSIMAvailable":"firstKey","Name":"onePair"}}
Wherein I get an unwanted object called map inserted in my main profileData object.
However when I print this inside the loop I get
{`"ioCinema":"firstValue","ioSIMAvailable":"firstKey","Name":"onePair"}`
Whish is exactly what I want inside profileData object, without nesting the map object.
How do I solve this?
"I am already aware that I can achieve this by converting the type of profileData in D class from JSONObject to String, which will induce escape characters - However I am looking for a generic solution"
EDIT:
map1 is constructed in two ways, depending on user input and both ways are as follows:
if (args.length >= 4 && args[1].equalsIgnoreCase("onePair")) {
map1 = new HashMap<>();
String key1 = args[2];
String value1 = args[3];
map1.put(key1, value1);
profileDataInJson = new JSONObject(gson.toJson(map1));
}
And:
if (args.length >= 1 && args[0].equalsIgnoreCase("update")) {
if (args.length >= 2)
profileData.setName(args[1] != null ? args[1] : "");
if (args.length >= 3)
profileData.setSIMAvailable(args[2] != null ? args[2] : "");
profileDataInJson = new JSONObject(profileData);
}
Signature: ProfileData profileData = new ProfileData();
The thing which puzzles me is when I try to traverse profileData and try to fetch the json object by name "map" I get a nullPointer exception

You don't need to use Gson to convert hashmap to a json object.
Simply use:
profileDataInJson = new JSONObject(map);

Add custom serializer to Gson, so that Gson serialize the org JSON as expected by you.
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(JSONObject.class, new JsonSerializer<JSONObject>() {
#Override
public JsonElement serialize(final JSONObject src, final Type typeOfSrc,
final JsonSerializationContext context) {
return new JsonParser().parse(src.toString()).getAsJsonObject();
}
});
gsonBuilder.create().toJson(map1);
This will return {"ioCinema":"firstValue","ioSIMAvailable":"firstKey","Name":"onePair"}

Related

Java Gson parse Json object to array

I'm trying to parse the below Json using the Gson lib in Java. When using other languages, such as C#, this JSON is parsed into an array, however it seems Gson converts this into a set of java attributes (which to be honest, makes more sense to me). Does anyone know if I can change this behaviour of the Gson lib?
{
"Outer": {
"0": {
"Attr1": 12345,
"Attr2": 67890
},
"1": {
"Attr1": 54321,
"Attr2": 09876
}
}
}
The below code demonstrates how Gson parses the array as a JsonObject. To be clear, I realise I've referenced outer as a JsonObject but I was just doing this to demonstrate the code. If I try and reference outer as an JsonArray, the code fails.
String json = "{\"Outer\": { \"0\": { \"Attr1\": 12345, \"Attr2\": 67890 }, \"1\": { \"Attr1\": 54321, \"Attr2\": 09876 }}}";
Gson gson = new GsonBuilder()
.disableHtmlEscaping()
.setLenient()
.serializeNulls()
.create();
JsonObject jo = gson.fromJson(json, JsonObject.class);
JsonObject outer = jo.getAsJsonObject("Outer");
System.out.println(outer);
System.out.println(outer.isJsonArray());
Result:
{"0":{"Attr1":12345,"Attr2":67890},"1":{"Attr1":54321,"Attr2":"09876"}}
false
//edit
I'm using this current simple Json as an example, however my application of this code will be to parse Json that's of varying and unknown shape. I therefore need Gson to automatically parse this to an array so that the isJsonArray returns true.
TL;DR: See "Using Deserializer" section at the bottom for parsing straight to array.
That JSON does not contain any arrays. An array would use the [...] JSON syntax.
Normally, a JSON object would map to a POJO, with the name in the name/value pairs mapping to a field of the POJO.
However, a JSON object can also be mapped to a Map, which is especially useful when the names are dynamic, since POJO fields are static.
Using Map
The JSON object with numeric values as names can be mapped to a Map<Integer, ?>, e.g. to parse that JSON to POJOs, do it like this:
class Root {
#SerializedName("Outer")
public Map<Integer, Outer> outer;
#Override
public String toString() {
return "Root[outer=" + this.outer + "]";
}
}
class Outer {
#SerializedName("Attr1")
public int attr1;
#SerializedName("Attr2")
public int attr2;
#Override
public String toString() {
return "Outer[attr1=" + this.attr1 + ", attr2=" + this.attr2 + "]";
}
}
Test
Gson gson = new GsonBuilder().create();
Root root;
try (BufferedReader in = Files.newBufferedReader(Paths.get("test.json"))) {
root = gson.fromJson(in, Root.class);
}
System.out.println(root);
Output
Root[outer={0=Outer[attr1=12345, attr2=67890], 1=Outer[attr1=54321, attr2=9876]}]
Get as Array
You can then add a helper method to the Root class to get that as an array:
public Outer[] getOuterAsArray() {
if (this.outer == null)
return null;
if (this.outer.isEmpty())
return new Outer[0];
int maxKey = this.outer.keySet().stream().mapToInt(Integer::intValue).max().getAsInt();
Outer[] arr = new Outer[maxKey + 1];
this.outer.forEach((k, v) -> arr[k] = v);
return arr;
}
Test
System.out.println(Arrays.toString(root.getOuterAsArray()));
Output
[Outer[attr1=12345, attr2=67890], Outer[attr1=54321, attr2=9876]]
Using Deserializer
However, it would likely be more useful if the conversion to array is done while parsing, so you need to write a JsonDeserializer and tell Gson about it using #JsonAdapter:
class Root {
#SerializedName("Outer")
#JsonAdapter(OuterArrayDeserializer.class)
public Outer[] outer;
#Override
public String toString() {
return "Root[outer=" + Arrays.toString(this.outer) + "]";
}
}
class OuterArrayDeserializer implements JsonDeserializer<Outer[]> {
#Override
public Outer[] deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
// Parse JSON array normally
if (json.isJsonArray())
return context.deserialize(json, Outer[].class);
// Parse JSON object using names as array indexes
JsonObject obj = json.getAsJsonObject();
if (obj.size() == 0)
return new Outer[0];
int maxKey = obj.keySet().stream().mapToInt(Integer::parseInt).max().getAsInt();
Outer[] arr = new Outer[maxKey + 1];
for (Entry<String, JsonElement> e : obj.entrySet())
arr[Integer.parseInt(e.getKey())] = context.deserialize(e.getValue(), Outer.class);
return arr;
}
}
Same Outer class and test code as above.
Output
Root[outer=[Outer[attr1=12345, attr2=67890], Outer[attr1=54321, attr2=9876]]]
I'll asume your JsonObject is a POJO class such like:
public Inner[] outer;
If you want an array of objects you can change your code to:
Inner[] jo = gson.fromJson(json, Inner[].class);
Jackson – Marshall String to JsonNode will be useful in your case.with following pom:-
//POM FILE
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.8</version>
</dependency>
//JAVA CODE
//read json file data to String
byte[] jsonData = Files.readAllBytes(Paths.get("employee.txt"));
//create ObjectMapper instance
ObjectMapper objectMapper = new ObjectMapper();
//read JSON like DOM Parser
JsonNode rootNode = objectMapper.readTree(jsonData);
JsonNode idNode = rootNode.path("id");
System.out.println("id = "+idNode.asInt());
JsonNode phoneNosNode = rootNode.path("phoneNumbers");
Iterator<JsonNode> elements = phoneNosNode.elements();
while(elements.hasNext()){
JsonNode phone = elements.next();
System.out.println("Phone No = "+phone.asLong());
}
You can use the JsonNode class's method findParent findValue and findPath which reduce your code as compare to another parsing library.
Please refer below code
1.To get an array of Objects (outerArray)
2.You can extract a JsonArray (outerJsonArray) containing values of inner objects in Outer (in case keys aren't significant for further use)
String json = "{\"Outer\": { \"0\": { \"Attr1\": 12345, \"Attr2\": 67890 }, \"1\": { \"Attr1\": 54321, \"Attr2\": 09876 }}}";
Gson gson = new GsonBuilder().disableHtmlEscaping().setLenient().serializeNulls().create();
JsonObject jo = gson.fromJson(json, JsonObject.class);
JsonObject outer = jo.getAsJsonObject("Outer");
Object[] outerArray = outer.entrySet().toArray();
// outerArray: [0={"Attr1":12345,"Attr2":67890}, 1={"Attr1":54321,"Attr2":"09876"}]
JsonArray outerJsonArray = new JsonArray();
outer.keySet().stream().forEach(key -> {
outerJsonArray.add(outer.get(key));
});
//jsonArray=[{"Attr1":12345,"Attr2":67890},{"Attr1":54321,"Attr2":"09876"}]
System.out.println(outer);
System.out.println(outerJsonArray.isJsonArray() + " " + outerJsonArray);

json.org Java: JSON array parsing bug [duplicate]

This question already has answers here:
How to parse JSON in Java
(36 answers)
Closed 4 years ago.
I have JSON object as follows:
member = "{interests : [{interestKey:Dogs}, {interestKey:Cats}]}";
In Java I want to parse the above json object and store the values in an arraylist.
I am seeking some code through which I can achieve this.
I'm assuming you want to store the interestKeys in a list.
Using the org.json library:
JSONObject obj = new JSONObject("{interests : [{interestKey:Dogs}, {interestKey:Cats}]}");
List<String> list = new ArrayList<String>();
JSONArray array = obj.getJSONArray("interests");
for(int i = 0 ; i < array.length() ; i++){
list.add(array.getJSONObject(i).getString("interestKey"));
}
public class JsonParsing {
public static Properties properties = null;
public static JSONObject jsonObject = null;
static {
properties = new Properties();
}
public static void main(String[] args) {
try {
JSONParser jsonParser = new JSONParser();
File file = new File("src/main/java/read.json");
Object object = jsonParser.parse(new FileReader(file));
jsonObject = (JSONObject) object;
parseJson(jsonObject);
} catch (Exception ex) {
ex.printStackTrace();
}
}
public static void getArray(Object object2) throws ParseException {
JSONArray jsonArr = (JSONArray) object2;
for (int k = 0; k < jsonArr.size(); k++) {
if (jsonArr.get(k) instanceof JSONObject) {
parseJson((JSONObject) jsonArr.get(k));
} else {
System.out.println(jsonArr.get(k));
}
}
}
public static void parseJson(JSONObject jsonObject) throws ParseException {
Set<Object> set = jsonObject.keySet();
Iterator<Object> iterator = set.iterator();
while (iterator.hasNext()) {
Object obj = iterator.next();
if (jsonObject.get(obj) instanceof JSONArray) {
System.out.println(obj.toString());
getArray(jsonObject.get(obj));
} else {
if (jsonObject.get(obj) instanceof JSONObject) {
parseJson((JSONObject) jsonObject.get(obj));
} else {
System.out.println(obj.toString() + "\t"
+ jsonObject.get(obj));
}
}
}
}}
Thank you so much to #Code in another answer. I can read any JSON file thanks to your code. Now, I'm trying to organize all the elements by levels, for could use them!
I was working with Android reading a JSON from an URL and the only I had to change was the lines
Set<Object> set = jsonObject.keySet();
Iterator<Object> iterator = set.iterator();
for
Iterator<?> iterator = jsonObject.keys();
I share my implementation, to help someone:
public void parseJson(JSONObject jsonObject) throws ParseException, JSONException {
Iterator<?> iterator = jsonObject.keys();
while (iterator.hasNext()) {
String obj = iterator.next().toString();
if (jsonObject.get(obj) instanceof JSONArray) {
//Toast.makeText(MainActivity.this, "Objeto: JSONArray", Toast.LENGTH_SHORT).show();
//System.out.println(obj.toString());
TextView txtView = new TextView(this);
txtView.setText(obj.toString());
layoutIzq.addView(txtView);
getArray(jsonObject.get(obj));
} else {
if (jsonObject.get(obj) instanceof JSONObject) {
//Toast.makeText(MainActivity.this, "Objeto: JSONObject", Toast.LENGTH_SHORT).show();
parseJson((JSONObject) jsonObject.get(obj));
} else {
//Toast.makeText(MainActivity.this, "Objeto: Value", Toast.LENGTH_SHORT).show();
//System.out.println(obj.toString() + "\t"+ jsonObject.get(obj));
TextView txtView = new TextView(this);
txtView.setText(obj.toString() + "\t"+ jsonObject.get(obj));
layoutIzq.addView(txtView);
}
}
}
}
1.) Create an arraylist of appropriate type, in this case i.e String
2.) Create a JSONObject while passing your string to JSONObject constructor as input
As JSONObject notation is represented by braces i.e {}
Where as JSONArray notation is represented by square brackets i.e []
3.) Retrieve JSONArray from JSONObject (created at 2nd step) using "interests" as index.
4.) Traverse JASONArray using loops upto the length of array provided by length() function
5.) Retrieve your JSONObjects from JSONArray using getJSONObject(index) function
6.) Fetch the data from JSONObject using index '"interestKey"'.
Note : JSON parsing uses the escape sequence for special nested characters if the json response (usually from other JSON response APIs) contains quotes (") like this
`"{"key":"value"}"`
should be like this
`"{\"key\":\"value\"}"`
so you can use JSONParser to achieve escaped sequence format for safety as
JSONParser parser = new JSONParser();
JSONObject json = (JSONObject) parser.parse(inputString);
Code :
JSONParser parser = new JSONParser();
String response = "{interests : [{interestKey:Dogs}, {interestKey:Cats}]}";
JSONObject jsonObj = (JSONObject) parser.parse(response);
or
JSONObject jsonObj = new JSONObject("{interests : [{interestKey:Dogs}, {interestKey:Cats}]}");
List<String> interestList = new ArrayList<String>();
JSONArray jsonArray = jsonObj.getJSONArray("interests");
for(int i = 0 ; i < jsonArray.length() ; i++){
interestList.add(jsonArray.getJSONObject(i).optString("interestKey"));
}
Note : Sometime you may see some exceptions when the values are not available in appropriate type or is there is no mapping key so in those cases when you are not sure about the presence of value so use optString, optInt, optBoolean etc which will simply return the default value if it is not present and even try to convert value to int if it is of string type and vice-versa so Simply No null or NumberFormat exceptions at all in case of missing key or value
From docs
Get an optional string associated with a key. It returns the
defaultValue if there is no such key.
public String optString(String key, String defaultValue) {
String missingKeyValue = json_data.optString("status","N/A");
// note there is no such key as "status" in response
// will return "N/A" if no key found
or To get empty string i.e "" if no key found then simply use
String missingKeyValue = json_data.optString("status");
// will return "" if no key found where "" is an empty string
Further reference to study
How to convert String to JSONObject in Java
Convert one array list item into multiple Items
There are many JSON libraries available in Java.
The most notorious ones are: Jackson, GSON, Genson, FastJson and org.json.
There are typically three things one should look at for choosing any library:
Performance
Ease of use (code is simple to write and legible) - that goes with features.
For mobile apps: dependency/jar size
Specifically for JSON libraries (and any serialization/deserialization libs), databinding is also usually of interest as it removes the need of writing boiler-plate code to pack/unpack the data.
For 1, see this benchmark: https://github.com/fabienrenaud/java-json-benchmark I did using JMH which compares (jackson, gson, genson, fastjson, org.json, jsonp) performance of serializers and deserializers using stream and databind APIs.
For 2, you can find numerous examples on the Internet. The benchmark above can also be used as a source of examples...
Quick takeaway of the benchmark: Jackson performs 5 to 6 times better than org.json and more than twice better than GSON.
For your particular example, the following code decodes your json with jackson:
public class MyObj {
private List<Interest> interests;
static final class Interest {
private String interestKey;
}
private static final ObjectMapper MAPPER = new ObjectMapper();
public static void main(String[] args) throws IOException {
MyObj o = JACKSON.readValue("{\"interests\": [{\"interestKey\": \"Dogs\"}, {\"interestKey\": \"Cats\" }]}", MyObj.class);
}
}
Let me know if you have any questions.

How to get JSON object by one of its values in Java?

Trying to parse multi-level JSON in Java.
Having JSON input in format like this:
{"object1":["0","1", ..., "n"],
"objects2":{
"x1":{"name":"y1","type":"z1","values":[19,20,21,22,23,24]}
"x2":{"name":"y2","type":"z2","values":[19,20,21,22,23,24]}
"x3":{"name":"y3","type":"z1","values":[19,20,21,22,23,24]}
"x4":{"name":"y4","type":"z2","values":[19,20,21,22,23,24]}
}
and need to get all objects from 2 by one of the attributes, e.g. get all objects with type = z1.
Using org.json*.
Tried to do something like this:
JSONObject GeneralSettings = new JSONObject(sb.toString()); //receiving and converting JSON;
JSONObject GeneralObjects = GeneralSettings.getJSONObject("objects2");
JSONObject p2;
JSONArray ObjectsAll = new JSONArray();
ObjectsAll = GeneralObjects.toJSONArray(GeneralObjects.names());
for (int i=0; i < GeneralObjects.length(); i++){
p2 = ObjectsAll.getJSONObject(i);
switch (p2.getString("type")) {
case "z1": NewJSONArray1.put(p2); //JSON array that should contain values with type z1.
break;
case "z2": NewJSONArray2.put(p2); //JSON array that should contain values with type z2.
default: System.out.println("error");
break;
}
}
}
But getting null pointer exception and overall method seems not to be so well.
Please advise, is there any way to make it easier or, what am I doing wrong?
If you're getting a NullPointerException it's most likely that you haven't initialized NewJSONArray1 and NewJSONArray2.
You didn't include their declaration, but you probably just need to do
NewJSONArray1=new JSONArray();
NewJSONArray2=new JSONArray();
before your loop.
Aside: by convention java variables should start with a lower case letter, e.g. newJSONArray1
public static void main(String[] args) {
String s =
"{\"object1\":[\"0\",\"1\",\"n\"]," +
"\"objects2\":{" +
"\"x1\":{\"name\":\"y1\",\"type\":\"z1\",\"values\":[19,20,21,22,23,24]}," +
"\"x2\":{\"name\":\"y2\",\"type\":\"z2\",\"values\":[19,20,21,22,23,24]}," +
"\"x3\":{\"name\":\"y3\",\"type\":\"z1\",\"values\":[19,20,21,22,23,24]}," +
"\"x4\":{\"name\":\"y4\",\"type\":\"z2\",\"values\":[19,20,21,22,23,24]}" +
"}}";
System.out.println(s);
JSONObject json = new JSONObject(s);
JSONObject object2 = json.optJSONObject("objects2");
if (object2 == null) {
return;
}
JSONArray result = new JSONArray();
for (Object key : object2.keySet()) {
JSONObject object = object2.getJSONObject(key.toString());
String type = object.optString("type");
if ("z1".equals(type)) {
System.out.println(object.toString());
result.put(object);
}
}
System.out.println(result);
}
You can always convert it to string and use json-path:
https://code.google.com/p/json-path/

Json parser converting null values to 0

I am using net.sf.json library and using it to parse my refMap like:
Map<String, Group> myMap = new HashMap<String,Group>();
myMap = this.getGroupValues();
JSONObject jsonObj = new JSONObject();
jsonObj.putAll(refMap);
File jsonFile = new File("./TempJson.txt");
FileWriter writer = new FileWriter(jsonFile);
fileWriter.write(jsonObj.toString());
where my Group class is defined as:
class Group {
Double val;
Integer num;
Section sectionObj;
//..getters & setters
}
The Problem is this.getGroupValues() returns some Group objects where val/num (wrapper classes) values are 'null' and then JsonObject parser converts it to 0 like: "val":0,"num":0
while if sectionObj is null , parser keeps it as null as "sectionObj":null
How to get null values for wrapper class Objects too in json file ?
I would suggest using a different JSON library. For example, GSON will leave null values out of the serialized JSON text. When it is deserialized by GSON, those missing values are set to null in the new object.
Consider this example:
public class Main {
public static void main(String[] args) {
Gson gson = new Gson();
final String json = gson.toJson(new Group());
System.out.println(json);
Group g = gson.fromJson(json, Group.class);
System.out.println(g);
}
public static class Group {
Double val;
Integer num;
Section sectionObj;
// Getters and setters...
#Override
public String toString() {
return "val: '" + val + "' num: '" + num
+ "' sectionObj: '" + sectionObj + "'";
}
}
public static class Section {}
}
which outputs the following:
{}
val: 'null' num: 'null' sectionObj: 'null'

Make Gson throw exception on parsing JSON with duplicated key

I'm parsing simple JSON object with Gson. I want it to throw some error when key name is duplicated. E.g.
{
a: 2,
a: 3
}
In my case, Gson parses such JSON and sets a to 3. I want it to throw some exception.
I know I can parse JSON as map, and then Gson throws exception in such case, but only if the duplicated key is not nested in the map. If I have e.g. JSON like this:
{
a: 2,
b: {
dup: 1,
dup: 2
}
}
Still, it is parsed without any exception and I have only one "dup" with value 2.
Can I somehow setup Gson to throw error in such case? Or to have duplicated entries in JsonObject instance, so that I can detect it myself (but I doubt that, as it would be invalid JsonObject)
Reproducible example
String json = "{\"a\":2, \"a\":3}";
Gson gson = new Gson();
JsonObject jsonObject = gson.fromJson(json, JsonObject.class);
System.out.println(jsonObject);
prints out
{"a":3}
1) You may edit the source of gson a little bit. This is just a suggestion to understand how things work. I don't advice you to use this on a real/production environment.
Gson uses com.google.gson.internal.LinkedTreeMap while parsing a json string to a JsonObject. For testing issues you can copy that class into your project with the same name and package name. And edit its put method to not allow duplicate keys.
#Override
public V put(K key, V value) {
if (key == null) {
throw new NullPointerException("key == null");
}
// my edit here
if(find(key, false) != null) {
throw new IllegalArgumentException("'" + key.toString() + "' is duplicate key for json!");
}
Node<K, V> created = find(key, true);
V result = created.value;
created.value = value;
return result;
}
2) Another clean solution is to define custom classes which are going to map to your json strings. Then write their custom TypeAdapters
3) Do it by using a Deserializer? I don't think it is possible. If you try to use it you'll see that you already have a jsonObject there which your duplicate keys are handled as one.
You can try this way:
String json = "{\"a\":2, \"a\":3}";
Gson gson = new Gson();
Type mapType = new TypeToken<Map<String, String>>() {}.getType();
Map<String, String> map = gson.fromJson(json, mapType);
And if json is more complex than JsonObject can be used as map value type:
Type mapType = new TypeToken<Map<String, JsonObject>>() {}.getType();

Categories

Resources