Given a JSON map like this:
{
"category1": [],
"category2": ["item1"],
"category3": ["item1", "item2"]
}
I would like to convert it into a Java ArrayList of Categories (not a Map), like this:
class Category {
final String categoryName;
final ArrayList<String> items;
public Category(String categoryName, ArrayList<String> items) {
this.categoryName = categoryName;
this.items = items;
}
}
ArrayList<Category> data = new ArrayList<>();
So I expect data
to look like this:
for (Category c: data) {
System.out.println(c.categoryName + ", " + c.items);
}
/**
Expected data =
category1, empty list [] or null
category2, [item1]
category2, [item1, item2]
**/
I've tried using the Gson library:
dependencies {
...
implementation 'com.google.code.gson:gson:2.8.6'
}
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonParser;
import com.google.gson.internal.LinkedTreeMap;
import com.google.gson.reflect.TypeToken;
import org.junit.Test;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
public class JsonConverterTest {
@Test
public void gsonMapToListTest() {
String json = "{\"category1\": [],\"category2\": [\"item1\"],\"category3\": [\"item1\", \"item2\"]}";
class Category {
final String categoryName;
final ArrayList<String> items;
public Category(String categoryName, ArrayList<String> items) {
this.categoryName = categoryName;
this.items = items;
}
}
ArrayList<Category> expectedData = new ArrayList<>();
expectedData.add(new Category("category1", new ArrayList<String>()));
expectedData.add(new Category("category2", new ArrayList<>(Arrays.asList("item1"))));
expectedData.add(new Category("category2", new ArrayList<>(Arrays.asList("item1", "item2"))));
System.out.println("Expected Data:");
for (Category c: expectedData) {
System.out.println(c.categoryName + ", " + c.items);
}
// This works, but it returns a Map instead of a List
LinkedTreeMap<String, ArrayList<String>> dataMap = new Gson().fromJson(json, LinkedTreeMap.class);
System.out.println("\n data as Map = " + dataMap);
// This causes "IllegalStateException: Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 path $"
Type listOfCategoriesType = new TypeToken<ArrayList<Category>>() {}.getType();
ArrayList<Category> data = new Gson().fromJson(json, listOfCategoriesType); // IllegalStateException here
assertThat(data, is(expectedData));
// This causes "java.lang.IllegalStateException: Not a JSON Array: {...}"
JsonArray jsonArray = JsonParser.parseString(json).getAsJsonArray(); // IllegalStateException here
data = new Gson().fromJson(jsonArray, listOfCategoriesType);
assertThat(data, is(expectedData));
}
}
Using the guides https://www.baeldung.com/gson-list and https://futurestud.io/tutorials/gson-mapping-of-maps , I can only convert the JSON map to a Java Map. But I get an IllegalStateException if I try to convert the JSON map to a Java List:
Type listOfCategoriesType = new TypeToken<ArrayList<Category>>() {}.getType();
ArrayList<Category> data = new Gson().fromJson(json, listOfCategoriesType); // IllegalStateException
or
JsonArray jsonArray = JsonParser.parseString(json).getAsJsonArray(); // IllegalStateException
ArrayList<Category> data2 = new Gson().fromJson(jsonArray, listOfCategoriesType);
So what is the correct way in Gson to convert the Json Map to a Java list, as per the unit test gsonMapToListTest()
above?
Easiest is to simply parse into Map
, then convert Map
to List<Category>
.
LinkedTreeMap<String, ArrayList<String>> dataMap = new Gson().fromJson(json,
new TypeToken<LinkedTreeMap<String, ArrayList<String>>>() {}.getType());
ArrayList<Category> dataList = new ArrayList<>();
for (Entry<String, ArrayList<String>> entry : dataMap.entrySet())
dataList.add(new Category(entry.getKey(), entry.getValue()));
The alternative is to write your own TypeAdapter
, or to use another JSON library.