I have an issue I haven't been able to find a solution to, because it's not quite straight forward I guess...
In my Java program, I receive a string like this (from an existing source, I can't change it, sadly):
["a", 1, "b", 2, ["c", "d", 3], 4]
This string is a valid JSON array (no object, so no key/value pairs!). The content of the array may be totally random (for instance, empty) but can also be multi-dimensional.
I need to find a way to read this in Java. For instance, input.get(4).get(2) should give me 3 in the above example. I don't really care if everything is casted to a string, but I need to find a way to reach the data through indices. Data may also be multiple characters, although my example shows only 1 per element.
How do I accomplish this? Thanks in advance!
Edit; I do know how many elements I expect in the root array, but there are a few dozen cases so I really don't want to program something different for each case
Pick a JSON library. Any of them.
The basic org.json:
JSONArray array = new JSONArray(yourString);
JSONArray arrayTwo = array.getJSONArray(4);
int i = arrayTwo.getInt(2);
Gson:
JsonElement element = new JsonParser().parse(json);
JsonArray array = element.getAsJsonArray();
JsonArray arrayTwo = array.get(4).getAsJsonArray();
int i = arrayTwo.get(2).getAsInt();
Etc, etc.
Using jackson 2.3.0:
import com.fasterxml.jackson.databind.*;
ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.readTree("[\"a\", 1, \"b\", 2, [\"c\", \"d\", 3], 4]");
int i = node.path(4).path(2).intValue();
System.out.println(i);
prints
3
Javadoc for JsonNode is here.
Related
I am using this library: https://github.com/jdereg/json-io
This is a simplified example of what I am trying to do, to reproduce the error:
import com.cedarsoftware.util.io.JsonReader;
Map args = new HashMap();
args.put(JsonReader.USE_MAPS, true);
List<String> a = (List<String>)JsonReader.jsonToJava("[\"1.1\",\"1.2\"]", args);
Runtime, this throws:
java.lang.ClassCastException: class [Ljava.lang.Object; cannot be cast to class java.util.List ([Ljava.lang.Object; and java.util.List are in module java.base of loader 'bootstrap')
Alternatively, the args can be left out (delete the two middle lines and remove the args argument to jsonToJava) with the same effect.
Reading dictionaries/maps works fine, for example this code prints "example" as expected:
Map<String,Object> fields = (Map<String,Object>)JsonReader.jsonToJava("{\"example\":true}");
for (Map.Entry<String,Object> field : fields.entrySet()) {
System.out.println(field.getKey());
}
It's only arrays that I cannot figure out how to read. The documentation that I've been able to find for the library is rather terse and does not show an example of this. Tracing the source code to see what kind of object it should be, I end up in a function named readArray, where it uses an ArrayList internally: https://github.com/jdereg/json-io/blob/master/src/main/java/com/cedarsoftware/util/io/JsonParser.java#L280. It looks like what I'm doing should be castable to List (I've also tried ArrayList just to be sure).
java.lang.ClassCastException: class [Ljava.lang.Object; cannot be cast to class java.util.List
Note that [Ljava.lang.Object represents the array type for object arrays, so it is Object[]. Since json arrays can contain anything, i.e. strings, booleans, numbers, objects or arrays, and in any combination any JSON parser would have 3 options:
Use Object as the array's element type. This would then result in Object[] as in your case.
Use some array element type provided by the user, i.e. if you know the array will contain only strings then you'd tell the parser that it should produce a String[] result. If the array contains anything other than strings that would most likely fail though.
A sophisticated parser might first collect the elements, determine their types and try to use the most special common types (if all elements are strings that would be String). However, the API can't reflect that because the compiler doesn't know what the json will contain at runtime. Hence the API could only use the first two options (an in many libraries like Jackon or Gson both are present) so the user would then have to cast either the array or the elements themselves and in that case there's little to be gained to make the parser that sophisticated.
Now you probably ask why the parser returns a Object[] instead of List<Object>. That's a decision made by the designer(s) of that library and I can only guess what the reasons are. However, json itself only knows arrays so the logical consequence would be to use Java arrays as well if no other type information is provided.
To extend the great answer of Thomas, here is a solution for human readable json:
Convert list of objects into json:
public String toJSONList(List<Foo> fooList) {
Map<String, Object> args = new HashMap<>();
args.put(JsonWriter.PRETTY_PRINT, true); // just to make sure it looks good
args.put(JsonWriter.TYPE, false); //disable ugly #type properties
return JsonWriter.objectToJson(fooList, args);
}
It generates nice human-readable json with list:
[
{
"id":100,
"name":"John"
},
{
"id":101,
"name":"Tirion"
}
]
Convert the above json back to List
public List<Foo> fromJSONList(String json) {
Map<String, Object> args = new HashMap<>();
args.put(JsonReader.USE_MAPS, true);
JsonReader jsonReader = new JsonReader();
List<Foo> result = new ArrayList<>();
Object[] array = (Object[])JsonReader.jsonToJava(json, args);
for (int i=0; i<array.length; i++) {
//we need to tell what is the type (remember that we have removed it above?)
((Map)array[i]).put("#type", "com.something.model.Foo");
Foo foo =(Foo)jsonReader.jsonObjectsToJava((JsonObject) array[i]);
result.add(foo);
}
return result;
}
I have a json api response that is structured similar to a csv file
{
"headers": ["a", "b", "c"],
"row1": [1, 2, 3],
"row2": [4, 5, 6]
}
I need to get a POJO for each row in that json object
Ideally I'd like to declare it like
class Row {
#SerializedName("a")
Integer a;
#SerializedName("b")
Integer b;
#SerializedName("c")
Integer c;
}
So my question is, can I write a custom deserializer so that I can do something like this? It would also be useful to serialize the other direction as well.
Gson gson = new Gson();
List<Row> rows = gson.fromJson(apiResponse, new List<Row>());
The problem not in deserialization.
My way to solve your issue.
Use defaulet deserializers for parsing.
Use mapstruct to convert parsed lists to objects
I am migratimg an old visual basic application to Android and as I progressed I ran into some problems i can't solve. (I'm new to java)
In VB there is something called Dictionary and after googling the equivalent in java I come to the conclusion the thing to use is HashMap.
I need to create a HashMap with a string as key and an int[] as object:
HashMap<String, int[]> hm
So far, so good. I learned that after creating my int[] I set the HashMap the following way...
int[] intArray = new int[23];
hm.put("myRandomString", intarray);
Now to the problem, how can I change the value on position x in my intArray?
I know I will use the key to find the intArray but anything I try give me an error.
Simple:
String someKey = "myRandomString";
int[] arrayFromMap = hm.get(someKey);
if (arrayFromMap != null) {
arrayFromMap[x] = y;
Beyond that, you could/should use methods such as:
if (hm.contains(someKey))
or
if (arrayFromMap.length > x)
to check for all the possible things that could go wrong here. Also pay attention to details such as:
int[] oneArray = { 1, 2 , 3};
hm.put("a", oneArray);
hm.put("b", oneArray);
which adds the same array using two different keys. When you know do get("a") and manipulate the corresponding array, the value for "b" changes, too!
You first have to get() the array:
int[] arrToBeModified = hm.get("myRandomString");
arrToBeModified[0] = 123; // Do your modifications here.
I have JSON array like this:
[{"BMW": [], "OPEL": [], "Mercedes": []}]
and I want to get
[{"Mercedes": [], "OPEL": [], "BMW": []}]
How can I make like this?
Since JSONObjects are not keeping their order in which you build them (has to do with how it's handled in memory) you should refactor the data.
[{"BMW": [], "OPEL": [], "Mercedes": []}]
to
[{"BMW":[]}, {"OPEL":[]}, {"Mercedes":[]}]
That still won't help you right away though since JSONArray in java doesn't appear to have reverse() method :(
So like I commented: build a new JSONArray in reverse order:
JSONArray newJsonArray = new JSONArray()
for (int i = jsonArray.length()-1; i>=0; i--) {
newJsonArray.put(jsonArray.get(i));
}
A small yet best solution to this is to store the JSON array in reverse order:
JSONArray array = response.getJSONArray("records");
for (int i = array.length()-1; i >= 0; i--) {
// Perform your regular JSON Parsing here
}
You dont need to edit / change your JSON
You have a JSON object, not an array. An object is comprised of key-value pairs, but these pairs are not ordered by key in any way. In fact, the order should not matter - the representation is the same.
From the ECMAScript spec, as copied here: http://interglacial.com/javascript_spec/a-4.html
4.3.3 Object An object is a member of the type Object. It is an unordered collection of properties each of which contains a primitive
value, object, or function. A function stored in a property of an
object is called a method.
I was wondering if it is possible to convert an Object into something else.
I have a Object which contains a series of numbers in a random order such as: 3, 4, 2, 5, 1 and wondering if I am able to turn it into an int[] or select certain elements from it, as in a number from the sequence?
EDIT:
so some of the code i have is:
//This contains all the different combinations of the numbers
ArrayList routePop4 = new ArrayList();
//This picks out the first one, just as a test
Object test = routePop4.get(0);
But the idea is that I want to loop through each element of test.
An Object cannot "contain a series of numbers". However many subclasses of Object, such as all of the Collections can "contain a series of numbers", and they come with a toArray() method to turn the contents of the collection into an array.
If you have a collection, but only have access to it as an Object, you need to cast it before you can work with it properly:
ArrayList<Integer> list = (ArrayList<Integer>)test;
Integer[] arr = list.toArray(new Integer[]{});
It's fairly rare in day-to-day Java to actually be working with variables cast as Object, if you are, it should be a red flag that you may be doing something wrong. You can use generics to allow objects that contain other objects to do so generically, like so:
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(1); // Can only add integers, list.add("a string") would fail at compile time
int n = list.get(0); // no need to cast, we know list only contains Integers
If you aren't using a Collection, you'll presumably need to roll your own, as Luke Taylor's answer suggests. That said, you'll get better answers if you can provide more information, the current text of your question doesn't make sense in a Java context.
After seeing your edit, I recommend taking advantage of generics.
When you declare an ArrayList you can indicate what kind of objects it's going to contain.
For example, if you know your ArrayList will contain Strings, you would do this:
List<String> myList = new ArrayList<String>();
If each element of your list is an array of Integers, you would do this:
List<Integer[]> listOfIntegerArrays = new ArrayList<Integer[]>();
Then you could get any element from your list and assign it to an Integer array like this:
Integer[] integerArray = listOfIntegerArrays.get(0);
Then you could iterate over every Integer in the list like this:
for (Integer loopInteger : integerArray) {
System.out.println("The value: " + loopInteger);
}
Some more reading on generics:
http://thegreyblog.blogspot.com/2011/03/java-generics-tutorial-part-i-basics.html
http://docs.oracle.com/javase/tutorial/java/generics/
You could do something like this:
int[] numbersFromObject = new int[yourObject.getAmountOfNumbers()];
// Initialize array with numbers from array
for(int i = 0; i < yourObject.getAmountOfNumbers(); i++) {
numbersFromObject[i] = yourObject.getNumber(i);
}
I'm not sure what methods your object contains, yet I'm sure you'll be able to adjust to the following mentioned above.
I hope this helps.