How to serialize a complex map in java to JSON - java

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.

Related

Javascript Objects but for Java

How do I write this in Java?
//js
const hello = {
foo: "bar",
test: "world",
name: "david"
}
I want have a very long object, then refer it back like hello[test] or hello[foo]
I've heard of hashmaps, but you can only create an empty one and then add elements into it.
I've got a really long list like that in js. How can I copy those into Java? Doing .put() one by one would take forever, and I don't think that's efficient.
And even if someone wrote a script to turn uwu: "owo" into hello.put("uwu", "owo");, it'd be ugly in the code with a big block of hello.put()s.
I also don't want to create a new file for that (it only has around 34 lines) and want to keep it in the code. Also, because I have three more like these with 20-40 keys and values in each of them, I don't want to create three extra files with just 30 lines in them. I also don't want to go into complexity of reading them.
Oh and also, I won't be changing the hashmap btw, just reading data like a constant.
In summary, can I do something like this in Java for long lists without doing .put()?
public HashMap<String, String> hello = new HashMap<String, String>(
"foo": "bar",
"test": "world",
"name": "david",
"uwu": "owo"
);
And refer to them like hello["name"]? I also don't want this thing.
public HashMap<String, String> hello = new HashMap<String, String>();
hello.put("foo", "bar");
hello.put("test", "world");
hello.put("name", "david");
hello.put("uwu", "owo");
//for 25 more lines
public HashMap<String, String> hello2 = new HashMap<String, String>();
hello2.put("stuff", "thing");
//... for around 20 more lines
//repeat for 3 more hashmaps
In modern Java (14 and later) you can use a record:
record Hello(String foo, String test, String world) { }
and create an instance like this:
final Hello hello = new Hello("bar", "world", "david");
You access the values like:
System.out.print(hello.foo());
Using a record has the advantage that your data is statically typed -- you can't mistype a key, or forget to remove usages of a key you've removed from the record.
IN Java 14 and beyond, I would recommand using a record, as explained in the other answer.
It's the safest and also probably the most efficient way.
For Java 9 to 14, you may use Map.of("hello", "world", "foo", "bar");.
But you may not be able to go beyond a certain number of key/value pairs.
For java 8 and below, or if you exceed the number of arguments allowed with Map.of, you don't have other choice than create an empty map and put key/value pairs one by one.
Note however that, performances aren't necessarily going to be worse.
You can of course reimplement your own version of Map.of with variable number of arguments.
Since you need something constant like, you can save those values in files and read from those files. For example save data in file in json format:
{
"foo": "bar",
"test": "world",
"name": "david"
}
Then parse this file to a Map.
public class Main {
public static void main(String[] args) throws IOException {
ObjectMapper mapper = new ObjectMapper();
Map<String, Object> map = mapper.readValue(ClassLoader.getSystemResourceAsStream("constants.json"), Map.class);
map.forEach((k, v) -> System.out.println(k + " -> " + v));
}
}
This example uses reading file as project resource and uses ObjectMapper to parse json to Map, but you can use any other tool for the same effect. If the data format is simple enough(string key to string value, no nested arrays, objects and such) you can save it in even simpler format and do the read, parse, add to map manually.

How to read out of a yml map<string, Map<string, string>> structure with cfg4j?

I have the following yml file
test-files:
testSets:
smoke:
- "a" : "b"
video:
- "c" : "d"
I am trying to read this out using cfg4j but I don't seem to get the interface right.
What I want to get is a map with all the testSets with all the data for that set inside it.
So e.g. if I have the following map construction:
map<String, Map<String, String>>
I want to have in the first string the value smoke, in the second one a and the last one b etc.
The interface that I already tried to us for this is:
interface TestSets{
Map<String, Map<String, String>> testSets;
}
But this doesn't work.
Has someone any idea about how to read this out using the cfg4j lib?

How to put hash map with different number of values in Volley onResponse

I need to do Volley POST method with this kind of body for onResponse method:
{"table":{"ma":1,"mb":2},"token":"access"}.
So basically I need HashMap called "params" that takes multiple key-value pairs as values for "table" key, and single value for"token" key, that goes inside Volley request.
I tried to do this,but it requires List for second argument for "token" value
Map<String, List<String>> params = new HashMap<>();
// Hash map for "ma"
HashMap<String, String> maParams = new HashMap<>();
maParams.put("ma", "1");
Hash map for "mb"
HashMap<String, String> mbParams= new HashMap<>();
mbParams.put("mb", "2");
// Table values
List<String> values = new ArrayList<>();
values.add(String.valueOf(maParams));
values.add(String.valueOf(mbParams));
params.put("table", beacons);
params.put("token", "access");
Any ideas how I can do that?
Thanks in advance.
What you require in your body actually looks like JSON. As I don't actually understand what you are trying to achieve with your code I would instead suggest that you take a look to famous JSON APIs such as Jackson or give a try to JSONObject . You will easily find ways to build an object just like you asked {"table":{"ma":1,"mb":2},"token":"access"}.
If overkill for you then just write pure Java to achieve what you want: build a map of tokens (instead of 2 Maps + 1 List) and write a formatting method to put it like you want in your params. Note: as {"ma":1,"mb":2} is not double-quoted in your example then what params must look like is more certainly a simple String (and not any kind of Map!). Actually this is JSON..
EDIT: you are strongly advised to have a look to these posts:
How to send a POST request with JSON body using Volley?
Android Volley POST Json to Server
Send POST request with JSON data using Volley
How to send a POST request using volley with string body?
Which all look like possible duplicates..

JSON Representation of HashMap

what is the Json representation of HashMap<String, String>?
I have tried this way but getting bad request error.
"userPreferences":{{"mobile":"yes"},{"email":"yes"}}
It should be like this
{ "userPreferences":{"mobile":"yes","email":"yes"} }
"userPreferences":{"mobile":"yes","email":"yes"}
The JSON you have would be invalid even as a JavaScript object, since you haven't defined property names for the two "inner" objects. A HashMap is basically a set of key-value pairs. Your JSON should look like:
"userPreferences": {
"mobile": "yes",
"email": "yes"
}

Java JSON/object to array

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".

Categories

Resources