How to perform deeper matching of keys and values with assertj - java

Say I have a class like this:
public class Character {
public Character(String name){
this.name = name;
}
private String name;
public String getName() { return name; }
}
And later, a Map
Map<Character, Integer> characterAges = new HashMap<Character, Integer>();
characterAges.put(new Character("Frodo"), 34);
Using assertj, what is the best way to test that characterAges includes the "Frodo" character? For the age, I can do:
assertThat(characterAges).hasValue(34);
And I know I could do:
assertThat(characterAges.keySet())
.extracting("name")
.contains("Frodo");
But then I lose my fluency. What I really want it something like this:
assertThat(characterAges)
.hasKey(key.extracting("name").contains("Frodo")
.hasValue(34);
Or even better, so that I can make sure my key and value match:
assertThat(characterAges)
.hasEntry(key.extracting("name").contains("Frodo"), 34);
Is something like this possible?

There is no easy solution for this. One way is to implement a custom Assertion for the character map. Here is a simple custom Assertion example for this problem:
public class CharacterMapAssert extends AbstractMapAssert<MapAssert<Character, Integer>, Map<Character, Integer>, Character, Integer> {
public CharacterMapAssert(Map<Character, Integer> actual) {
super(actual, CharacterMapAssert.class);
}
public static CharacterMapAssert assertThat(Map<Character, Integer> actual) {
return new CharacterMapAssert(actual);
}
public CharacterMapAssert hasNameWithAge(String name, int age) {
isNotNull();
for (Map.Entry<Character, Integer> entrySet : actual.entrySet()) {
if (entrySet.getKey().getName().contains(name) && (int) entrySet.getValue() == age) {
return this;
}
}
String msg = String.format("entry with name %s and age %s does not exist", name, age);
throw new AssertionError(msg);
}
}
In the test case:
assertThat(characterAges).hasNameWithAge("Frodo", 34);
Be aware that you have for every custom data structure to write your own assertion. For you Character class you can generate a assertion with the AssertJ assertions generator.
Update Java 8
With Java 8 can also the Lambda Expression used
assertThat(characterAges).matches(
(Map<Character, Integer> t)
-> t.entrySet().stream().anyMatch((Map.Entry<Character, Integer> t1)
-> "Frodo".equals(t1.getKey().getName()) && 34 == t1.getValue()),
"is Frodo and 34 years old"
);

You can also do something like this:
assertThat(characterAges).contains(entry("Frodo", 34), ...);
See https://github.com/joel-costigliola/assertj-core/wiki/New-and-noteworthy#new-map-assertions

The following methods of AbstractMapAssert will work for you:
containsExactlyEntriesOf Verifies that the actual map contains only the entries of the given map and nothing else, in order. This should be used with TreeMap. HashMap will not guarantee order and your test will randomly fail.
containsExactlyInAnyOrderEntriesOf Verifies that the actual map contains only the given entries and nothing else, in any order. This will work with HashMap.
import java.util.Map;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
public class TestMapAssertions {
#Test
public void testCreateMap() {
//Call
Map<String, String> actual = ClassUnderTest.createMap();
//Assert
Assertions.assertThat(actual).containsExactlyInAnyOrderEntriesOf(
Map.of("Key1", "Value1", "Key2", "Value2")
);
}
public static class ClassUnderTest {
public static Map<String, String> createMap() {
return Map.of("Key1", "Value1", "Key2", "Value2");
}
}
}

Since 3.6.0, you can use hasEntrySatisfying:
assertThat(characterAges)
.hasSize(1)
.hasEntrySatisfying(aKey, e ->
assertThat(e)
.isEqualTo(99.99)
);
In your case, if you cannot use the key for lookup you can use Condition-based hasEntrySatisfying (more verbose).

I am not sure about the date/version of introduction, but there are a bunch of assertions in MapAssert now. From http://joel-costigliola.github.io/assertj/core-8/api/org/assertj/core/api/MapAssert.html:
contains(Map.Entry... entries)
- Verifies that the actual map contains the given entries, in any order.
containsAnyOf(Map.Entry... entries)
- Verifies that the actual map contains at least one of the given entries.
containsExactly(Map.Entry... entries)
- Verifies that the actual map contains only the given entries and nothing else, in order.
containsKeys(KEY... keys)
- Verifies that the actual map contains the given keys.
containsOnly(Map.Entry... entries)
- Verifies that the actual map contains only the given entries and nothing else, in any order.
containsOnlyKeys(KEY... keys)
- Verifies that the actual map contains only the given keys and nothing else, in any order.
containsValues(VALUE... values)
- Verifies that the actual map contains the given values.
doesNotContain(Map.Entry... entries)
- Verifies that the actual map does not contain the given entries.
doesNotContainKeys(KEY... keys)
- Verifies that the actual map does not contain any of the given keys.
extracting(Function,Object>... extractors)
- Uses the given Functions to extract the values from the object under test into a list, this new list becoming the object under test.
For your example, containsExactly() should do the trick.

If you didn't want to go down the custom assert route, and could access the instances of the characters in the SUT (system under test) in the test, another option could be
In the SUT:
Character frodo = new Character("Frodo");
characterAges.put(frodo, 34);
And in the test
MapEntry frodoAge34 = MapEntry.entry(frodo, 34);
assertThat(characterAges).contains(frodoAge34);

What about using .entrySet() with .extracting()?
assertThat(characterAges.entrySet())
.extracting(
entry -> entry.getKey().getName(),
Map.Entry::getValue)
.contains(tuple("Frodo", 34));

Below is my example for asserting the nested map of response from actuator endpoint.
Below is the sample response from actuator endpoint. Response is a json response with two root level keys named "git" and "build" both of them are nested json. Below is an example of testing the build json structure
{
"git": {
"branch": "my-test-branch",
"build": {
"time": "2022-03-08T12:43:00Z",
"version": "0.0.1-SNAPSHOT",
"user": {
"name": "gituser",
"email": "gituseremail#domain.com"
},
"host": "hostName"
}
},
"build": {
"artifact": "test-project",
"name": "test-name",
"time": "2022-03-08T12:45:07.389Z",
"version": "0.0.1-SNAPSHOT",
"group": "com.exmaple"
}
}
Below is the test.
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class MyApplicationTest {
#Autowired
private TestRestTemplate restTemplate;
#Test
void testInfoActuatorEndpoint() {
Map responseMap = this.restTemplate.getForObject("/actuator/info", Map.class);
assertThat(responseMap)
.hasSize(2)
.containsKey("git")
.containsKey("build")
.extracting("build")
.hasFieldOrPropertyWithValue("artifact", "test-project")
.hasFieldOrPropertyWithValue("name", "test-name")
.hasFieldOrPropertyWithValue("version", "0.0.1-SNAPSHOT")
.hasFieldOrPropertyWithValue("group", "com.example");
}
}

Related

Retrieving nested key:value pairs in Java

I am wondering how to build a nested Map and then retrieve the nested key:value pairs. This is how I created the map to be nested.
//Create List of Nested HashMaps
List<Map> nestedMap = new ArrayList<>();
nestedMap.add(building3Map);
nestedMap.add(building31Map);
nestedMap.add(buildingHFTFMap);
System.out.println("The nested map is: " + nestedMap);
This is the system output for the following code:
The nested map is: [{buildingname=[Building 3], buildingid=[3]}, {buildingname=[Building 31], buildingid=[1]}, {buildingname=[HFTF], buildingid=[4]}]
This is correct as I want a list of maps. But the next step is what is confusing to me. When I try to build the outer layer Map as below:
HashMap<String, List<Map>> queryMap = new HashMap<>();
queryMap.put("buildings", nestedMap);
System.out.println(queryMap.get("buildings.buildingid"));
I get a system output of null when attempting the .get("buildings.buildingid") method. Ideally, I need the output to look like this:
[[3, 1, 4]]
Where it returns all values with a key of "buildings.buildingid" in an array. I am new to coding so please let me know if I'm making any fundamental errors and how I can create a nested Map where I can access the inner layer or if there is another method I should be using.
I think you are making it way too complicated than it should be. you can store your data in a simple map where the ids are the keys for example and the names are the values. So you only need to read the keys or the values from the map to get your result.
Map<Integer, String> myMap = new HashMap<>();
myMap.put(3, "Building 3");
myMap.put(31, "Building 31");
myMap.put(4, "HFTF");
System.out.println(myMap.keySet());
System.out.println(myMap.values());
However, Java is an object-oriented language. If it makes sense for your use case you might want to create a custom class "Building" instead of frantically trying to store your data in nested data structures. See below for an example of how it might look using a custom class:
import java.util.ArrayList;
import java.util.List;
public class Example {
public static void main(String args[]) {
List<Building> buildings = new ArrayList<>();
buildings.add(new Building(3, "Building 3"));
buildings.add(new Building(31, "Building 31"));
buildings.add(new Building(4, "HFTF"));
List<Integer> buildingIds = new ArrayList<>();
buildings.forEach(building -> buildingIds.add(building.getBuildingId()));
List<String> buildingNames = new ArrayList<>();
buildings.forEach(building -> buildingNames.add(building.getBuildingName()));
System.out.println(buildingIds);
System.out.println(buildingNames);
}
public static class Building {
int buildingId;
String buildingName;
public Building(final int buildingId, final String buildingName) {
this.buildingId = buildingId;
this.buildingName = buildingName;
}
public int getBuildingId() {
return buildingId;
}
public void setBuildingId(final int buildingId) {
this.buildingId = buildingId;
}
public String getBuildingName() {
return buildingName;
}
public void setBuildingName(final String buildingName) {
this.buildingName = buildingName;
}
}
}
queryMap.get("buildings.buildingid") returns null, because queryMap only contains a value under the key
buildings. A HashMap can only access the value using the same key it was stored under. The key and the value is not processed in any further way.
A simple alternative could be
queryMap.get("buildings").stream() // Create a Stream<Map>
.map(building -> building.get("buildingid")) // Create Stream<String> by extracting the buildingid values.
.collect(Collectors.toList()); // Collect Stream into a List<String> which contains the buildingid's
If you don't like this solution you could take a deeper look into property expressions, and write your own map implementation that can resolve these expressions. But it will be a lot of work to get it working correctly...

How do I get the key from Map in a List and do a get rest request in Java

I want to do a get request and want to get all the jobs who have the "teacher" in it. In this case the map value 2 and 3.
How do I get there if I execute the code below then I only get the value from the Map 2. The best way is over a List? But how do I do it?
I have this method.
#GET
#Path("jobs")
public Job getJobsId(#QueryParam("id") int id, #QueryParam("description") String description) {
for (final Map.Entry<Integer, Job> entry : jobs.entrySet()) {
if (entry.getValue().getDescription().toLowerCase().contains(description.toLowerCase())) {
System.out.println("I DID IT");
System.out.println(entry.getKey());
return berufe.get(entry.getKey());
}
}
return berufe.get(id);
}
and this Map:
jobs.put(1, new jobs(1, 1337, "student"));
jobs.put(2, new jobs(2, 420, "teacher"));
jobs.put(3, new jobs(3, 69, "schoolteacher"));
---------------------------------EDIT----------------------------------
If I do this:
#GET
#Path("jobs")
public Collection<Job> getJobsId(#QueryParam("id") int id, #QueryParam("description") String description) {
final Set<Beruf> result = new HashSet<>();
for (final Map.Entry<Integer, Job> entry : jobs.entrySet()) {
if (entry.getValue().getDescription().toLowerCase().contains(description.toLowerCase()) == true) {
result.add(jobs.get(entry.getKey()));
} else {
return jobs.values();
}
}
return result;
}
I get with a discription all Map values back and without I get an Error.
What do I do wrong here?
Your method getJobsId() returns on the first item it finds (inside the if statement), that's why you only get one result.
The usual pattern to collect a number of results would be to instantiate a suitable Collection before the for loop, and add each item that is found to this collection. Then, after the for loop, return the collection. I don't understand your code completely, but it would be something similar to the below (I'm sure it won't work if you just copy-paste, so read and understand what is going on ;-) ):
Set<Beruf> result = new HashSet<>();
for (final Map.Entry<Integer, Job> entry : jobs.entrySet()) {
if (entry.getValue().getDescription().toLowerCase().contains(description.toLowerCase())) {
result.add(berufe.get(entry.getKey()));
}
}
return result;
Since Java 8, it has been much more concise (readable) to use the Streams interface and call .filter (and maybe .map) on it, then .collect the stream to a suitable collection and return that. Something similar to:
return jobs.entrySet().stream()
.filter(entry -> (entry.getValue().getDescription().toLowerCase().contains(description.toLowerCase())))
.map(entry -> berufe.get(entry.getKey()))
.collect(Collectors.toSet());
A function can return only one element. If there more than one hit, you have to return an object that can take on multiple elements. As you already mentioned a list, as an representant of a collection, can do this job. For your use case is a map a better option:
#GET
#Path("jobs")
public Job getJobsId(#QueryParam("id") int id, #QueryParam("description") String description)
{
//Declare an object that can take on multiple elements
var Map<Job> jobList = new HashMap<>();
for (final Map.Entry<Integer, Job> entry : jobs.entrySet())
{
if (entry.getValue().getDescription().toLowerCase().contains(description.toLowerCase()))
{
System.out.println("I DID IT");
System.out.println(entry.getKey());
jobList.put(entry.getKey(), job.get(entry.getKey()));
}
}
return jobList.get(id);
}
First of all, you should do is design the API correctly, for example:
#GET
#Path("jobs")
public List<Integer> searchJobsIdByDescription(#QueryParam("description") String description) {
...
}
Important: you must give a suitable name to the method (f.e: searchJobsIdByDescription), your method should only do one thing, so you declare only the parameters needed to do that thing (f.e: (#QueryParam("description") String description)) and return the expected type (List<Integer>).
Pay attention to the annotation #Path("jobs"), you have to avoid matching the previous ones
Next, you should carefully read #frIV's answer

How can I compare a String with an Array of Strings to get the Value from a Map?

I'm having a BiMap with a String as key and an array of Strings as value.
Now i'm trying to get with a single String (which is part of the value array) the key.
private static BiMap<String, String[]> map = ImmutableBiMap.<String, String[]>builder().build();
static {
map.put("000", new String[] {"CH", "CHE", "Switzerland"});
map.put("001", new String[] {"US", "USA", "United States of America"});
map.put("002", new String[] {"IT", "ITA", "Italy"});
}
And in the next method i'm trying to search with "CH" to get "000" (which does not work).
private static String getKey(Map<String,String[]> map, String find) {
Map<String[], String> inversedMap = map.inverse();
if(inversedMap.containsKey() {
return inversedMap.get(find);
}
return null;
}
Is there a way to 'find' the key like this, without that i need to search with an array like this: String[] find = new String[] {"CH", "CHE", "Switzerland"};
All the values and keys are unique, so there is expected only a single result.
And i'm searching always for the first value in the array, f.ex. "CH" or "US".
No, there is no way to find the key like you want. You have to either change the way you store the data to support all the different lookup method you need or go through all keys one by one (at which point making an inverse map makes no sense an you can just go through the Map entries).
A trivial approach would be a purpose built class that contains several maps.
In case you have a case to find smth. by value (not by key) then you could use for loop in case you do not worry about performance. Otherwise, you should wrap this BiMap with a wrapper and add addtional Map with val -> key:
public final class CountryCache {
private final Map<String, String[]> codeNames = new HashMap<>();
private final Map<String, String> nameCode = new HashMap<>();
{
codeNames.put("000", new String[] { "CH", "CHE", "Switzerland" });
codeNames.put("001", new String[] { "US", "USA", "United States of America" });
codeNames.put("002", new String[] { "IT", "ITA", "Italy" });
codeNames.forEach((code, names) -> Arrays.stream(names).forEach(name -> nameCode.put(name, code)));
}
private static final CountryCache INSTANCE = new CountryCache();
public static CountryCache getInstance() {
return INSTANCE;
}
private CountryCache() {
}
public String findByName(String name) {
return nameCode.get(name);
}
}

What is the reasoning on modelling a class to represent JSON data and do I need to?

I have come across this question on StackOverflow which asks about converting JSON to Java. The answer shows that another class is modelled to represent the JSON data as well as an object being created and I don't understand why.
Does that object now contain all the information after Gson reads the content or only one key/value pair? If it only contains 1 key/value pair, I'm assuming I would need to create multiple objects for the JSON that I have below which I can the use a loop to iterate over and add the values to a drop down menu?
{
"1": "Annie",
"2": "Olaf",
"3": "Galio",
"4": "TwistedFate",
"5": "XinZhao",
"6": "Urgot",
"7": "Leblanc",
"8": "Vladimir",
"9": "FiddleSticks",
"10": "Kayle",
"11": "MasterYi",
"12": "Alistar",
"13": "Ryze",
"14": "Sion",
"15": "Sivir",
"16": "Soraka",
"17": "Teemo",
"18": "Tristana",
"19": "Warwick",
"20": "Nunu"
}
Essentially what I am aiming to do is:
1) Create a list of names with the Values.
2) Sort the list of names (as it comes unsorted) in alphabetical order
3) Loop through the list and add each name to a drop down menu
4) When a name in the drop down menu is selected, the key associated with that value is passed to another url which receives more data.
Sorry if this is unclear. I've spent a couple of hours trying to understand how to get elements from JSON and display it, as well as trying to create a list where I can use the key to display information the name but have had no luck except for using a for-each loop.
Let's use Jackson's feature that allows you to map any property to a single method (you don't really need a getter here I believe). Just swap the key and value in this universal setter, and add to a TreeMap, which is already sorted by key (name). Then you can output the keys (names) in the alphabetical order and get an ID by name easily.
public static void main(String[] args) throws IOException {
String json = "....."; // your JSON string here
com.fasterxml.jackson.databind.ObjectMapper mapper =
new com.fasterxml.jackson.databind.ObjectMapper();
ReverseMap pairs = mapper.readValue(json, ReverseMap.class);
for (Map.Entry<Object, String> entry : pairs.getValues().entrySet()) {
System.out.println(entry.getKey() + ":" + entry.getValue());
}
}
public class ReverseMap {
private TreeMap<Object, String> mapping = new TreeMap<>();
#com.fasterxml.jackson.annotation.JsonAnySetter
public void add(String name, Object value) {
mapping.put(value, name);
}
public Map<Object, String> getValues() {
return mapping;
}
}
Gson Bean Mapping Solution
Okay, what you have is a bit unusual for a JSON object; the keys (the numbers in your case) essentially represent properties of their contained object. That's workable, but you have to understand that, for example, when looking for "Annie" in the JSON object, if you use Gson to map to a "bean" class, which we'll call Data (as in the linked example), then you'd have to create a data object like so:
class Data {
private String _1;
// ...
private String _20;
public String get1() { return _1; }
public void set1(String _1) { this._1 = _1; }
// ...
public String get20() { return _20; }
public void set20(String _20) { this._20 = _20; }
}
And by using Data data = new Gson().fromJson(myJsonString, Data.class); on the given string, you'd be able to find "Annie" by calling... uh... data.get1()?
Clearly, this isn't a good solution.
Better Solutions
Since your data doesn't follow the typical format for a JSON object, you have two options:
If you can, refactor your JSON representation to a more verbose, but better representation for parsing.
Use a different approach to parse the existing JSON.
Solution 1: Changing the JSON representation
Refactoring the JSON would result in an object that (preferably) would look like this:
{
"champions" : [
{
"index" : 1,
"name" : "Annie"
},
{
"index" : 2,
"name" : "Olaf"
},
// ...
]
}
This could map easily to a couple of beans that look like this:
class Data {
private List<Champion> champions;
// TODO getters and setters
}
class Champion {
private int index;
private String name;
// TODO getters and setters
}
However, this adds a lot of unnecessary clutter to the JSON object, and isn't really necessary with only two fields per champion (the name, and their index).
You could simplify that further like so:
{
"champions" : [
"Annie",
"Olaf",
// ...
]
}
The bean class for that would then be:
class Data {
private List<String> champions;
// TODO getters and setters
}
Much simpler, but still requires a change to the JSON you're getting, which in some situations isn't possible. If you used this, though, you could also get rid of the "bean" class entirely, via:
List<String> champions = (List<String>) new Gson().fromJson(myJsonString, new TypeToken<List<String>>(){}.getType());
Solution 2: Changing how the JSON is parsed
The arguably better and cleaner solution is just to change how the JSON is parsed.
The goal here (if I understand you correctly) is to parse the JSON and spit out a collection of strings representing each champion's name, accessible by the numeric index of the champion in the JSON representation.
As such, and because of the way the JSON object is laid out as a simple mapping of strings to strings, we can use Gson to pipe directly into a Map<String, Object>, like so:
Map<String, String> mappedValues = new Gson().fromJson(myJsonString, Map.class);
String anniesName = mappedValues.get("1"); // "Annie"
String olafsName = mappedValues.get("2"); // "Olaf"
boolean hasTwentyOneElements = mappedValues.containsKey("21"); // false
This is shorter, requires no "bean" classes, and keeps the original JSON representation. The downside is that you can't easily tell whether the indices of each entry are correct and consistent; ie. if someone types in the wrong number, or deletes one of the entries.
To get a container of all keys, you just use mappedValues.keySet(), and to get a container of all key-value pairs, you use mappedValues.entrySet(), which gives you a Set<Map.Entry<String, String>>. Both of those can be iterated over, and may be in random order (I'm not sure whether the underlying Map implementation preserves insertion order or not).
To get the index for a given name (ie. champ), you'd use something similar to the following:
String index = null;
for (Map.Entry<String, String> entry : mappedValues.entrySet()) {
if (champ.equals(entry.getValue())) {
index = entry.getKey();
break;
}
}
Of course, you'd have to check to see if index is null after this, and handle that appropriately, but it's easily doable.
EDIT: #vempo's answer provides a cleaner, more efficient lookup strategy by means of inverting the map (although the answer is written for Jackson, instead of Gson); an adaptation of this for Gson is as follows (and yes, there is a vastly superior version in java-8, left out for sake of availability):
public Map<String, String> invertMap(Map<String, String> input) {
Map<String, String> newMap = new LinkedTreeMap<String, String>(); // TODO Pick optimal storage class
for (Map.Entry<String, String> entry : input.entrySet()) {
newMap.put(entry.getValue(), entry.getKey());
}
return newMap;
}
// ...
Map<String, String> mappedValues = invertMap(new Gson().fromJson(myJsonString, Map.class));
String annieIndex = mappedValues.get("Annie"); // "1"
String olafIndex = mappedValues.get("Olaf"); // "2"
It's worth noting that this sacrifices efficiency of constructing the map by effectively building it twice (once by Gson and once more to invert), but it makes value lookup much more efficient.

Creating a Java Object from JSON Object containing JSON Maps using GSON

So, I've been doing GSON for a while, but I just now ran into the issue of using JSON Maps, which as I understand it are basically just Key Value pairs where he value is a JSON object.
To give you an idea where I'm coming from, here's my JSON
{
"configs":[
{
"com.hp.sdn.adm.alert.impl.AlertManager":{
"trim.alert.age":{
"def_val":"14",
"desc":"Days an alert remains in storage (1 - 31)",
"val":"14"
},
"trim.enabled":{
"def_val":"true",
"desc":"Allow trim operation (true/false)",
"val":"true"
},
"trim.frequency":{
"def_val":"24",
"desc":"Frequency in hours of trim operations (8 - 168)",
"val":"24"
}
}
},
{
"com.hp.sdn.adm.auditlog.impl.AuditLogManager":{
"trim.auditlog.age":{
"def_val":"365",
"desc":"Days an audit log remains in storage (31 - 1870)",
"val":"365"
},
"trim.enabled":{
"def_val":"true",
"desc":"Allow trim operation (true/false)",
"val":"true"
},
"trim.frequency":{
"def_val":"24",
"desc":"Frequency in hours of trim operations (8 - 168)",
"val":"24"
}
}
}
]
}
All of those com.hp.sdn... things are dynamic, as in I won't know the key names until Runtime. I figured I can just use a HashMap for this and GSON would figure it out, but I'm not sure what I would name the field...
Here are my classes that I have so far
package com.wicomb.sdn.models.configs;
import java.util.HashMap;
import com.wicomb.sdn.types.Model;
public class ConfigResponse extends Model {
private ConfigGroup[] configs;
}
package com.wicomb.sdn.models.configs;
import com.wicomb.sdn.types.Model;
public class ConfigGroup extends Model {
private HashMap<String,Config> ????;
}
TL;DR How should I write the Java Class to let Gson know how to handle a Json property that I don't know the name of.. And lots of them.
You can feed Gson with a HashMap (or if children order is important a LinkedHashMap) than you iterate over the entries or the keys normally as you would do to any other map.
In the code below I use the following json as input:
{
"test": 123,
"nested":{
"nested-1": 1,
"nested-2": 2
}
}
And the code looks like these:
public void testGson() {
String input = "{\"test\": 123, \"nested\": {\"nested-1\": 1, \"nested-2\": 2}}";
LinkedHashMap<String, Object> json = new Gson().fromJson(input, LinkedHashMap.class);
// iterating
for(Map.Entry<String, Object> entry : json.entrySet()){
System.out.println(entry.getKey() + " -> " + entry.getValue());
}
// testing values
System.out.println(json.get("test")); // should be 123
Map<String, Object> nested = (Map<String, Object>) json.get("nested");
System.out.println(nested.get("nested-1")); // should be 1
System.out.println(nested.get("nested-2")); // should be 2
}

Categories

Resources