Java JSON/object to array - java

I have a question about type casting. I have the following JSON String:
{"server":"clients","method":"whoIs","arguments":["hello"]}
I am parsing it to the following Map<String, Object>.
{arguments=[hello], method=whoIs, server=clients}
It is now possible to do the following:
request.get("arguments");
This works fine. But I need to get the array that is stored in the arguments. How can I accomplish this? I tried (for example) the following:
System.out.println(request.get("arguments")[0]);
But of course this didn't work..
How would this be possible?

Most likely, value is a java.util.List. So you would access it like:
System.out.println(((List<?>) request.get("arguments")).get(0));
But for more convenient access, perhaps have a look at Jackson, and specifically its Tree Model:
JsonNode root = new ObjectMapper().readTree(source);
System.out.println(root.get("arguments").get(0));
Jackson can of course bind to a regular Map too, which would be done like:
Map<?,?> map = new ObjectMapper().readValue(source, Map.class);
But accessing Maps is a bit less convenient due to casts, and inability to gracefully handle nulls.

Maybe
System.out.println( ((Object[]) request.get("arguments")) [0]);
? You could also try casting this to a String[].
Anyway, there are more civilized ways of parsing JSON, such as http://code.google.com/p/google-gson/.

StaxMan is correct that the type of the JSON array in Java is List (with ArrayList as implementation), assuming that the JSON is deserialized similar to
Map<String, Object> map = JSONParser.defaultJSONParser().parse(Map.class, jsonInput);
It is easy to determine such things by simply inspecting the types.
Map<String, Object> map = JSONParser.defaultJSONParser().parse(Map.class, jsonInput);
System.out.println(map);
for (String key : map.keySet())
{
Object value = map.get(key);
System.out.printf("%s=%s (type:%s)\n", key, value, value.getClass());
}
Output:
{arguments=[hello], method=whoIs, server=clients}
arguments=[hello] (type:class java.util.ArrayList)
method=whoIs (type:class java.lang.String)
server=clients (type:class java.lang.String)
Also, the svenson documentation on basic JSON parsing describes that "[b]y default, arrays will be parsed into java.util.List instances".

Related

Can't get value from JSONObject

So I am using the simple json library to perform some json operations. Right now I can construct a JSONObject from a json string but I am not able to get the value from the object I created.
For example if I do something like:
String value = (String) jsonRecord.get("Key");
I will get an error saying:
java.lang.ClassCastException: org.json.simple.JSONObject cannot be cast to java.lang.String
I removed the type cast to string and it works in IntelliJ. However, when I do this at command line it gives me an error saying:
error: incompatible types: Object cannot be converted to String
The schema is as follows:
{
"myArray": {
"array": ["Decaf mocha", "Vanilla mocha", "Chai Latte"]
},
"Item": {
"string": "Decaf macha"
}
}
Update: the toString() fixed the problem. But when I tried to get the array I am getting:
java.lang.ClassCastException: org.json.simple.JSONObject cannot be cast to org.json.simple.JSONArray
Can someone please suggest how to fix the problem? Thanks!
Your value is a JSONObject, not a string. Your error message makes that quite clear. If you really want it as a string, use
String value = jsonRecord.get("Key").toString();
You can pass any object to System.out.println, not just strings, but to actually turn it to a string, you need to call toString() yourself.
However, if you're expecting an actual String as the Key, and not a JSONObject, then you should take a second look at your JSON, because you're doing something wrong.
UPDATE:
Okay, looking at your schema, I see the problem. Instead of mapping the keys to values directly, your JSON maps keys to objects which then contain values. So to get the array in the JSON you posted, instead of
value = jsonRecord.get("myArray")
you would use
JSONArray value = jsonRecord.getJSONObject("myArray").getJSONArray("array");
and for the string, you would use
String value = jsonRecord.getJSONObject("Item").getString("string");
Just need to add the current "org.json" dependency would resolve your issue as latest version holds get() method (JSONObject class) which returns Object.
Below is my maven dependency:-
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20190722</version>
</dependency>
Below is my code which performs get key operation.
public void iterateJsonObject(JSONObject jsonObj) {
jsonObj.keySet().forEach(keyStr ->
{
Object keyvalue = jsonObj.get(keyStr);
System.out.println("key: "+ keyStr + " value: " + keyvalue);
});
}
You can prevent the ClassCastException by using Generics, because Generics provide compile time checks and can be used to develop type-safe applications.
You are not converting the json record value that you are fetching into a string.
String value = String.valueOf(jsonRecord.get("Key"));
his should fix your problem. You can also use toString() method but I personally prefer String.valueOf() over it because at times toString() tends to give garbage value.

Deserialize/Parse JSON in Java

I am searching a good and dynamic way to parse JSON in Java.
I've seen things such as:
List<String> list = new ArrayList<String>();
JSONArray array = obj.getJSONArray("test");
for(int i = 0 ; i < array.length() ; i++){
list.add(array.getJSONObject(i).getString("testKey"));
}
But that's not what I'm searching. In C# I had something like that:
dynamic results = JsonConvert.DeserializeObject<dynamic>(json);
info.Text = results["test"]["testKey"];
Here's an example of my JSON:
{"date":"07.05.2017 11:44",
"monday":{"1":{"subject":"test","room":"test","status":"test"}}}
So for example I would like to make:
results["monday"]["1"]["subject"];
I hope someone understands my problem and can help me.
Thanks in advance!
The core Java runtime does not offer a JSON parser (edit: technically, it does, see bottom of answer), so you will need a library. See Jackson, Gson, perhaps others.
Even with that, you will not get the dynamic features you want, because Java is statically typed. Example with Jackson:
ObjectMapper mapper = new ObjectMapper();
Map<String, Object> map = mapper.readValue(json, new TypeReference<Map<String, Object>>(){});
map.get("monday").get("1").get("subject");
^^^
This fails because the result of get("monday") is Object, not Map
The "right" approach in Java-land, would be to create a class (or set of classes) that represents your JSON model, and pass it to the JSON parser's "object mapper". But you said "dynamic" so I'm not exploring this here.
So you'll need to cast to Map when you know it's not primitive values:
((Map<String,Map<String,String>>)map.get("monday")).get("1").get("subject");
This works but with a warning about unchecked cast...
All in all, Java is not a dynamic language and I see no way to do exactly what you want (perhaps I'm missing approaches that are still slightly easier than what I have suggested).
Are you limited to Java-the-language or Java-the-platform? In the latter case you can use a dynamic language for the Java platform, such as Groovy, who has excellent features to parse JSON.
EDIT: a funny alternative is to use Java's own JavaScript implementation. This works and is easy and dynamic, but I don't know if it's "good":
String json = "{\"date\":\"07.05.2017 11:44\",\n" +
"\"monday\":{\"1\":{\"subject\":\"test\",\"room\":\"test\",\"status\":\"test\"}}}";
ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
engine.put("data", json);
System.out.println(engine.eval("JSON.parse(data)['monday']['1']['subject']"));
If you are sure about the value you want to get then you can do following as well :
String str = "{\"date\":\"07.05.2017 11:44\", \"monday\":{\"1\":{\"subject\":\"test\",\"room\":\"test\",\"status\":\"test\"}}}";
JSONObject results= new JSONObject(str);
String str1 = results.getJSONObject("monday").getJSONObject("1").getString("subject");
System.out.println(str1);
For array kind of results, we have to write logic for that. In this case org.json library is used.
You can use GCON library:
https://github.com/google/gson
Very good for parsing JSON objects.

How to maintain JSON's order in Groovy's JsonSlurper?

I am reading a simple JSON....
{"A":0,"B":0,"C":2,"D":0,"F":5}
into a map using JsonSlurper in Groovy...
Map gradeDistributon = jsonSlurper.parseText(jsonString)
But when iterating over this map with a closure..
gradeDistributon.each{ entry ->
println "From map got key ${entry.key}"
I am seeing the keys are not in the order they were in the original JSON, for example 'C' comes first. I think this is because Map does not maintain insertion order in Java. Is there a way I can keep the order of the original JSON?
If it means reading the JSON in a different way (instead of into a Map with JsonSlurper) then I am fine with that if you can show me how.
You can set JVM system property jdk.map.althashing.threshold to make JsonSlurper to use a LinkedHashMap instead of TreeMap as the internal Map implementation, e.g. -Djdk.map.althashing.threshold=512.
The reason is in source code of groovy.json.internal.LazyMap used by JsonSlurper.
private static final String JDK_MAP_ALTHASHING_SYSPROP = System.getProperty("jdk.map.althashing.threshold");
private void buildIfNeeded() {
if (map == null) {
/** added to avoid hash collision attack. */
if (Sys.is1_7OrLater() && JDK_MAP_ALTHASHING_SYSPROP != null) {
map = new LinkedHashMap<String, Object>(size, 0.01f);
} else {
map = new TreeMap<String, Object>();
}
}
}
Please note this solution should be used as a hack as it depends on Groovy's internal implementation details. So this behavior may change in future version of Groovy.
See my blog post for details.
So it was just a matter of sorting the keys after JsonSlurper built the Map, for that I just read into a TreeMap which sorts the keys by default..
TreeMap gradeDistributon = jsonSlurper.parseText(jsonString)
I can't reproduce your behaviour with groovy 2.4.5 but you can try using LinkedHashMap (allow to iterate over map keys maintaining the order in which the entries were inserted):
import groovy.json.*
def jsonText = '''
{"A":0,"B":0,"C":2,"D":0,"F":5,"G":7,"H":9}
'''
LinkedHashMap json = new JsonSlurper().parseText(jsonText)
json.each{ entry ->
println "${entry.key}"
}
NOTE: as stated by #XenoN the JsonSlurper() sort the json keys during the parsing process so independently of the input order (ie. {"H":0,"B":0,"A":2,"D":0,"G":5,"F":7,"C":9}) the output of JsonSlurper will be always: {"A":2,"B":0,"C":9,"D":0,"F":7,"G":5,"H":0}.
Using the LinkedHashMap instead of a HashMap we preserve the order given by JsonSlurper.
I run the same code on Groovy 2.4.x and on 3.0.x.
On 2.4 the order is preserved,but on 3.0 is sorted asc by default.
use the JsonSluperClassic().parse() instead it will preserve the order

How to serialize a complex map in java to JSON

I am facing a problem while serializing map of maps(or lists) into JSON. It can be upto any level. I am using GSON to convert map into JSON but I am not getting the desired output. Consider an example below:
Map<String, String> map = new HashMap<String, String>();
map.put("key1", "value1");
map.put("key2", new ArrayList<String>(){{add("value2");add("value3");}});
When I serialize this map, I get to the following output:
{
"key1" : "value1",
"key2" : "[value2, value3]"
}
It gets worse when I have map inside a map. The output which I want is:
{
"key1" : "value1",
"key2" : ["value2", "value3"]
}
Any idea how to get such output? I have already tried ObjectMapper and GsonBuilder.registerTypeAdapter but haven't got success so far.
Thanks in advance.
I don't think it's valid to store an ArrayList in a HashMap where a String is expected. I would've said that that code should fail, but I guess it must be succeeding, since it's not failing for you, and so what I'm assuming is happening is that when you insert it into the HashMap Java is actually calling toString() on it behind the scenes to cast it to String. So the value sitting in the HashMap is not actually an array; it was a string from the moment it was inserted. Thus, you're losing information that can't be salvaged downstream from the lossage.

Detect type of JSON attribute

JSON:
{"attribute1":11, "attribute2":"string atribute"}
I want to detect what kind of type are attribute1 and attribute2:
attribute1 is integer
attribute2 is string
jsonObject.getAttributeType("attribute2"); // should output: string/integer/boolean.
It was very easy to achieve in PHP or OBJC. Suggestions?
(I'm assuming that the Android for the org.json package is that same as you can find on the json.org site ... here.)
The only method on a JSONObject that will give you the underlying value ... without coercing it ... is JSONObject.get(name). If name is known, the result is the object that represents the value internally. I haven't done a comprehensive trawl of the code, but I think it can only be one of the following types:
Boolean, Long, Double, String, JSONArray, JSONObject
You should be able to discriminate these using instanceof.
But should be asking yourself if this is the right thing to do. The normal way to deal with JSON object attributes via the JSONObject API is to use the methods that coerce them into the type that you expect. In most cases, it doesn't matter if a number is sent as 42 or 42.0 or "42" ... and it is best not to be picky if the intent is easy to determine.
Another solution you can use the jackson library to do this,
import com.fasterxml.jackson.databind.JsonNode;
import com.github.fge.jackson.JsonLoader;
//Defining the JSON object
JSON json = {"attribute1":11, "attribute2":"string atribute"};
//Get the needed attribute
String value = json.get("attribute1");
//Convert the attribute to JsonNode
JsonNode value = JsonLoader.fromString(value);
//Then you can check type as below
value.isObject();
value.isArray();
value.isDouble();
value.isTextual();
value.isInt()

Categories

Resources