I have a Json Structure something like this
{
"name" : "abcd",
"details" : [{"city":"string", "zipcode":"integer"}],
"name" : "qwert",
"details" : [{"address":"long", "state":"string"}]
}
And my java code looks like below
public class JsonTest {
public static void main(String[] args) throws JsonParseException, IOException {
JsonFactory jf = new JsonFactory();
JsonParser jp = jf.createParser(new File("C:\\sample.json"));
while (jp.nextToken() != JsonToken.END_OBJECT)
{
String jsonField = jp.getCurrentName();
if ("name".equalsIgnoreCase(jsonField))
{
jp.nextToken();
System.out.println(jp.getText());
}
if ("details".equalsIgnoreCase(jsonField))
{
jp.nextToken();
while (jp.nextToken() != JsonToken.END_ARRAY)
{
jp.nextToken();
String field = jp.getText();
System.out.println(field);
}
}
}
}
}
All am trying to do is parse the whole json token by token and get the text. But the line while (jp.nextToken() != JsonToken.END_OBJECT) fails when it encounters a '}' in the array. Am just stuck here since yesterday and experimenting with several other tricks but nothing works out. Do we have another way doing this? Is my JSON structure looks okay? Please tell me where am going wrong. Thanks!
First of all:
[Doe]s my JSON structure look[s] okay?
Yes and no. while your JSON is legal, you are going to have a problem because of duplicate keys:
{
"name": "xxx",
"name": "yyy"
}
The behaviour of a JSON parser in this event is unpredictable, even RFC 7159 says so (yes, RFC 4627 has been superseded). Unfortunately, duplicate keys are still not forbidden.
So, it is legal, but not sane. Use an array instead:
[ { "name": "xxx", "details": "whatever" }, "etc" ]
This aside, reading JSON with Jackson is done as this:
final ObjectMapper mapper = new ObjectMapper();
final JsonNode node = mapper.readTree(yourFileHere);
You can then use the full power of JsonNode.
You cannot have fields with the same name in a json, well you can but it could not be parsed as in your case.
I assume you have an array of users with name and details fileds for each user.
Here is a candidate Java class corresponding to your User class:
public class TestUser implements Serializable {
String name;
List<Map<String, String>> details = new ArrayList<Map<String, String>>();
TestUser() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Map<String, String>> getDetails() {
return details;
}
public void setDetails(List<Map<String, String>> details) {
this.details = details;
}
}
with reverse engineering lets see how is a list of users are de-serialized;
public static void main(String[] args) throws IOException {
List<TestUser> testUsers = new ArrayList<TestUser>();
TestUser testUser1 = new TestUser();
testUser1.name = "abcd";
HashMap<String, String> detailsHashMap = new HashMap<String, String>();
detailsHashMap.put("city", "string");
detailsHashMap.put("zipcode", "integer");
testUser1.details.add(detailsHashMap);
testUsers.add(testUser1);
TestUser testUser2 = new TestUser();
testUser2.name = "qwert";
detailsHashMap = new HashMap<String, String>();
detailsHashMap.put("address", "long");
detailsHashMap.put("state", "string");
testUser2.details.add(detailsHashMap);
testUsers.add(testUser2);
ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(testUsers));
}
and the outcome of the users list is:
[ {
"name" : "abcd",
"details" : [ {
"zipcode" : "integer",
"city" : "string"
} ]
}, {
"name" : "qwert",
"details" : [ {
"address" : "long",
"state" : "string"
} ]
} ]
As you can see it is different from your JSON string.
UPDATE
If you want to hold names and details in two separate containers...;
public static class Container {
List<String> names = new ArrayList<String>();
Map<String, String> details = new HashMap<String, String>();
public Container() {
}
public List<String> getNames() {
return names;
}
public void setNames(List<String> names) {
this.names = names;
}
public Map<String, String> getDetails() {
return details;
}
public void setDetails(Map<String, String> details) {
this.details = details;
}
}
....
public static void main(String[] args) throws IOException {
Container container = new Container();
container.names.add("abcd");
container.names.add("qwert");
container.details.put("city", "string");
container.details.put("zipcode", "integer");
container.details.put("address", "long");
container.details.put("state", "string");
ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(container));
}
and the outcome JSON is;
{
"names" : [ "abcd", "qwert" ],
"details" : {
"address" : "long",
"zipcode" : "integer",
"state" : "string",
"city" : "string"
}
}
Related
I am trying to reformat this json file to a different format. I never used jackson or gson before. I get the idea of it but I don't know how to implement it.
So what I have is a json file: file.json that contains:
{
"Fruits": [
{
"name": "avocado",
"organic": true
},
{
"name": "mango",
"organic": true
}
]
}
What I want is to get in this format:
{
"List Fruits":{
"Fruits": [
{
"name": "avocado",
"organic": true
},
{
"name": "mango",
"organic": true
}
]
}
}
Somehow add the "List Fruits" in the json file.
I am trying to use the jackson api but I don't know how.
Assign the JSON to String variable, for example assign the above JSON to variable called json:
String json = "..." // here put your JSON text;
Prepare classes for your objects:
class Fruit {
private String name;
private boolean organic;
}
class Fruits {
private List<Fruit> fruits;
}
then use Gson to convert JSON to your objects:
Gson gson = new Gson();
Fruits fruits = gson.fromJson(json, Fruits.class);
Next prepare wrapper class ListOfFruits for your fruits object:
class ListOfFruits {
private Fruits listOfFruits;
public ListOfFruits(Fruits fruits) {
listOfFruits = fruits;
}
}
Next pack your fruits object into another one:
ListOfFruits lof = new ListOfFruits(fruits);
And finally generate back the output JSON:
String newJson = gson.toJson(lof);
You do not need to create POJO model for reading and updating JSON. Using Jackson, you can read whole JSON payload to JsonNode, create a Map with required key and serialising to JSON back. See below example:
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import java.io.File;
import java.util.Collections;
import java.util.Map;
public class JsonApp {
public static void main(String[] args) throws Exception {
File jsonFile = new File("./resource/test.json").getAbsoluteFile();
ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);
JsonNode root = mapper.readTree(jsonFile);
Map<String, JsonNode> output = Collections.singletonMap("List Fruits", root);
System.out.println(mapper.writeValueAsString(output));
}
}
Above code prints:
{
"List Fruits" : {
"Fruits" : [ {
"name" : "avocado",
"organic" : true
}, {
"name" : "mango",
"organic" : true
} ]
}
}
I would highly recommend going through the documentations of Jackson or Gson libraries as you mentioned you are new.
I have created a sample git repo for this item. This sample uses Jackson API.
Visit https://github.com/rajramo61/jsonwrapper
final InputStream fileData = ClassLoader.getSystemResourceAsStream("file.json");
ObjectMapper mapper = new ObjectMapper();
InitialJson initialJson = mapper.readValue(fileData, InitialJson.class);
System.out.println(mapper.writeValueAsString(initialJson));
final FinalJson finalJson = new FinalJson();
finalJson.setListOfFruits(initialJson);
System.out.println(mapper.writeValueAsString(finalJson));
This is the Fruit class.
public class Fruit {
private String name;
private boolean organic;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean getOrganic() {
return organic;
}
public void setOrganic(boolean organic) {
this.organic = organic;
}
#Override
public String toString() {
return "Fruit{" +
"name='" + name + '\'' +
", organic=" + organic +
'}';
}
}
Here is FinalJson class detail. This is the class will wrap the initial json loaded from jsn file.
public class FinalJson {
private InitialJson listOfFruits;
#JsonProperty("List Fruits")
public InitialJson getListOfFruits() {
return listOfFruits;
}
public void setListOfFruits(InitialJson listOfFruits) {
this.listOfFruits = listOfFruits;
}
}
Here is InitialJson class detail. This is the class pulls data from json file.
public class InitialJson {
private List<Fruit> fruits;
#JsonProperty("Fruits")
public List<Fruit> getFruits() {
return fruits;
}
public void setFruits(List<Fruit> fruits) {
this.fruits = fruits;
}
#Override
public String toString() {
return "InitialJson{" +
"fruits=" + fruits +
'}';
}
}
You can fork the repo and close this in local and it should work fine.
My API needs to return a list of entry objects from the JSON below. I am using jersey and jackson. I would ideally like to only create a java class PermissionEnty , and my API to return a list of PermissionEntry objects from the JSON. I am not able to deserialize using the below approach? Can someone advise what could be the issue? I have added UNWRAP_ROOT_VALUE so I presume the 'list' node it ignored, and I would get items below 'list' node.
public class PermissionEntry {
private String id;
private String displayName;
private String memberType;
}
and the json;
{
"list": {
"pagination": {
"count": 5,
"hasMoreItems": false,
},
"entries": [
{
"entry": {
"displayName": "norma",
"id": "norma",
"memberType": "PERSON"
}
},
{
"entry": {
"displayName": "clara",
"id": "clara",
"memberType": "PERSON"
}
},
{
"entry": {
"displayName": "michael",
"id": "mich",
"memberType": "PERSON"
}
}
]
}
}
PermissionEntries
public class PermissionEntries {
#JsonProperty(value = "entries")
#JsonDeserialize(using = PermissionEntryDeserializer.class)
private List<PermissionEntry> entries;
public List<PermissionEntry> getEntries() {
return entries;
}
public void setEntries(List<PermissionEntry> entries) {
this.entries = entries;
}
}
Below is the deserializer that I am using
public class PermissionEntryDeserializer extends JsonDeserializer<List<PermissionEntry>> {
private static final String ENTRY = "entries";
private static final ObjectMapper mapper = new ObjectMapper()
.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);
private static final CollectionType collectionType =
TypeFactory
.defaultInstance()
.constructCollectionType(List.class, PermissionEntry.class);
#Override
public List<PermissionEntry> deserialize(JsonParser jsonParser, DeserializationContext deserializationContext)
throws IOException {
ObjectNode objectNode = mapper.readTree(jsonParser);
JsonNode nodeEntries = objectNode.get(ENTRY);
if (null == nodeEntries // if no ENTRY node could be found
|| !nodeEntries.isArray() // or ENTRY node is not an array
|| !nodeEntries.elements().hasNext()) // or ENTRY node doesn't contain any entry
return null;
return mapper.readerFor(collectionType).readValue(nodeEntries);
}
}
Service API
public Optional<List<PermissionEntry>> getPermissionsForGroup(String groupName) {
Response response = getTarget()
.path("/api/group/" + groupName + "/members")
.request()
.get();
PermissionEntries list = response.readEntity(PermissionEntries.class);
}
I don't understand what you mean in this question 'Can someone please tell me how many java classes do I need to create to get a list of entries.' but you had already an entry object called PermissionEntry. You will have the list of this object.
This is the jersey client of your data with jakcson .
ClientConfig clientConfig = new DefaultClientConfig();
clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE);
Client client = Client.create(clientConfig);
String URL = "http://{{host}}:{{port}}/entry";
WebResource webResourceGet = client.resource(URL);
ClientResponse response = webResourceGet.accept("application/json").get(ClientResponse.class);
String output = response.getEntity(String.class);
ResponseList responseList= mapper.readValue(output , ResponseList .class);//here is the permissionEntry list that you wil have
Also, you should create an object given name as Pagination for pagination that is in the json data. You can make an another object that includes List<PermissionEntry> and Pagination called ResponseList.
I have an enhanced question regarding Flatten a JSON string to Map using Gson or Jackson.
My scenario included duplicated keys, so the solution in the above question will cause some duplicated keys overwritten. So I am thinking to construct keys by combining each level's key together.
So how to achieve that?
For example:
{
"id" : "123",
"name" : "Tom",
"class" : {
"subject" : "Math",
"teacher" : "Jack"
}
}
I want to get the Map:
"id" : "123",
"name" : "Tom",
"class.subject" : "Math",
"class.teacher" : "Jack"
************************Update Solution*************************************
Based on #Manos Nikolaidis's answer, I am able to achieve the following solution by considering ArrayNode.
public void processJsonString(String jsonString) throws Exception {
ObjectMapper mapper = new ObjectMapper();
ArrayNode arrayNode = (ArrayNode) mapper.readTree(jsonString);
processArrayNode(arrayNode);
}
private void processObjectNode(JsonNode jsonNode) {
Map<String, String> result = new HashMap<>();
Iterator<Map.Entry<String, JsonNode>> iterator = jsonNode.fields();
iterator.forEachRemaining(node -> mapAppender(result, node, new ArrayList<String>()));
}
private void processArrayNode(ArrayNode arrayNode) {
for (JsonNode jsonNode : arrayNode) {
processObjectNode(jsonNode);
}
}
private void mapAppender(Map<String, String> result, Map.Entry<String, JsonNode> node, List<String> names) {
names.add(node.getKey());
if (node.getValue().isTextual()) {
String name = names.stream().collect(Collectors.joining("."));
result.put(name, node.getValue().asText());
} else if (node.getValue().isArray()) {
processArrayNode((ArrayNode) node.getValue());
} else if (node.getValue().isNull()) {
String name = names.stream().collect(Collectors.joining("."));
result.put(name, null);
} else {
node.getValue().fields()
.forEachRemaining(nested -> mapAppender(result, nested, new ArrayList<>(names)));
}
}
You can get the JSON as JsonNode and go through all fields recursively and add key and value field to a Map. When a value is an object instead of string you can add the field name to List to be joined with periods when a string is finally encountered. First create (for readability) a separate method that add Json fields to a Map:
void mapAppender(Map<String, String> result, Entry<String, JsonNode> node, List<String> names) {
names.add(node.getKey());
if (node.getValue().isTextual()) {
String name = names.stream().collect(joining("."));
result.put(name, node.getValue().asText());
} else {
node.getValue().fields()
.forEachRemaining(nested -> mapAppender(result, nested, new ArrayList<>(names)));
}
}
and use it like this:
ObjectMapper mapper = new ObjectMapper();
Map<String, String> result = new HashMap<>();
mapper.readTree(json).fields()
.forEachRemaining(node -> mapAppender(result, node, new ArrayList<String>()));
Where fields() returns an Iterator. Beware of StackOverflowErrors and perhaps low performance for deeply nested JSON.
I resolved this using below simple code, Only think is need to download jettison and flattener.JsonFlattener library
import java.util.Map;
import org.codehaus.jettison.json.JSONObject;
import com.github.wnameless.json.flattener.JsonFlattener;
public class test {
public static void main(String[] args) {
String jsonString = "{\"id\" : \"123\",\"name\" : \"Tom\",\"class\" : {\"subject\" : \"Math\",\"teacher\" : \"Jack\"}}";
JSONObject jsonObject = new JSONObject();
String flattenedJson = JsonFlattener.flatten(jsonString);
Map<String, Object> flattenedJsonMap = JsonFlattener.flattenAsMap(jsonString);
System.out.println(flattenedJsonMap);
}
}
Reference link : https://github.com/wnameless/json-flattener
I have an enhanced question regarding Flatten a JSON string to Map using Gson or Jackson.
My scenario included duplicated keys, so the solution in the above question will cause some duplicated keys overwritten. So I am thinking to construct keys by combining each level's key together.
So how to achieve that?
For example:
{
"id" : "123",
"name" : "Tom",
"class" : {
"subject" : "Math",
"teacher" : "Jack"
}
}
I want to get the Map:
"id" : "123",
"name" : "Tom",
"class.subject" : "Math",
"class.teacher" : "Jack"
************************Update Solution*************************************
Based on #Manos Nikolaidis's answer, I am able to achieve the following solution by considering ArrayNode.
public void processJsonString(String jsonString) throws Exception {
ObjectMapper mapper = new ObjectMapper();
ArrayNode arrayNode = (ArrayNode) mapper.readTree(jsonString);
processArrayNode(arrayNode);
}
private void processObjectNode(JsonNode jsonNode) {
Map<String, String> result = new HashMap<>();
Iterator<Map.Entry<String, JsonNode>> iterator = jsonNode.fields();
iterator.forEachRemaining(node -> mapAppender(result, node, new ArrayList<String>()));
}
private void processArrayNode(ArrayNode arrayNode) {
for (JsonNode jsonNode : arrayNode) {
processObjectNode(jsonNode);
}
}
private void mapAppender(Map<String, String> result, Map.Entry<String, JsonNode> node, List<String> names) {
names.add(node.getKey());
if (node.getValue().isTextual()) {
String name = names.stream().collect(Collectors.joining("."));
result.put(name, node.getValue().asText());
} else if (node.getValue().isArray()) {
processArrayNode((ArrayNode) node.getValue());
} else if (node.getValue().isNull()) {
String name = names.stream().collect(Collectors.joining("."));
result.put(name, null);
} else {
node.getValue().fields()
.forEachRemaining(nested -> mapAppender(result, nested, new ArrayList<>(names)));
}
}
You can get the JSON as JsonNode and go through all fields recursively and add key and value field to a Map. When a value is an object instead of string you can add the field name to List to be joined with periods when a string is finally encountered. First create (for readability) a separate method that add Json fields to a Map:
void mapAppender(Map<String, String> result, Entry<String, JsonNode> node, List<String> names) {
names.add(node.getKey());
if (node.getValue().isTextual()) {
String name = names.stream().collect(joining("."));
result.put(name, node.getValue().asText());
} else {
node.getValue().fields()
.forEachRemaining(nested -> mapAppender(result, nested, new ArrayList<>(names)));
}
}
and use it like this:
ObjectMapper mapper = new ObjectMapper();
Map<String, String> result = new HashMap<>();
mapper.readTree(json).fields()
.forEachRemaining(node -> mapAppender(result, node, new ArrayList<String>()));
Where fields() returns an Iterator. Beware of StackOverflowErrors and perhaps low performance for deeply nested JSON.
I resolved this using below simple code, Only think is need to download jettison and flattener.JsonFlattener library
import java.util.Map;
import org.codehaus.jettison.json.JSONObject;
import com.github.wnameless.json.flattener.JsonFlattener;
public class test {
public static void main(String[] args) {
String jsonString = "{\"id\" : \"123\",\"name\" : \"Tom\",\"class\" : {\"subject\" : \"Math\",\"teacher\" : \"Jack\"}}";
JSONObject jsonObject = new JSONObject();
String flattenedJson = JsonFlattener.flatten(jsonString);
Map<String, Object> flattenedJsonMap = JsonFlattener.flattenAsMap(jsonString);
System.out.println(flattenedJsonMap);
}
}
Reference link : https://github.com/wnameless/json-flattener
I just need a quick advice, as i am a total beginner with JSON.
I get the following response from a webserver, which i store in a String:
{
"station62":[
{
"departureTime":1982,
"delay":"-1.0",
"line":"6",
"stationName":"randomname",
"direction":2
}
],
"station63":[
{
"departureTime":1234,
"delay":"-1.0",
"line":"87",
"stationName":"anotherrandomname",
"direction":2
}
],
"station64":[
{
"departureTime":4542,
"delay":"-1.0",
"line":"4",
"stationName":"yetanotherrandomname",
"direction":2
}
],
"station65":[
{
"departureTime":1232,
"delay":"-1.0",
"line":"23",
"stationName":"onemorerandomname",
"direction":2
}
]
}
(Sorry, i dont know how the indent works on here.)
The response is longer, but for this example it is shortened. So what i need is to parse the information of each of these "station"-objects.
I dont need the "station62"-String, i only need "departureTime", "delay", "line", "stationName" and "direction" in a java-object.
I have read this, but i couldnt make it work: https://stackoverflow.com/a/16378782
I am a total beginner, so any help would be really appreciated.
Edit: Here is my code:
I made a wrapper class just like in the example link above. I played with the map types a bit, but no luck so far.
public class ServerResponse
{
private Map<String, ArrayList<Station>> stationsInResponse = new HashMap<String, ArrayList<Station>>();
public Map<String, ArrayList<Station>> getStationsInResponse()
{
return stationsInResponse;
}
public void setStationsInResponse(Map<String, ArrayList<Station>> stationsInResponse)
{
this.stationsInResponse = stationsInResponse;
}
}
The problem is, that this map does not get filled by the gson.fromJSON(...)-call i am showing below. The map size is always zero.
Station class looks like this:
public class Station
{
String line;
String stationName;
String departureTime;
String direction;
String delay;
// getters and setters are there aswell
}
And what i am trying to do is
Gson gson = new Gson();
ServerResponse response = gson.fromJson(jsonString, ServerResponse.class);
where "jsonString" contains the JSON response as a string.
I hope that code shows what i need to do, it should be pretty simple but i am just not good enough in JSON.
EDIT 2
Would i need my JSON to be like this?
{"stationsInResponse": {
"station62": [{
"departureTime": 1922,
"delay": "-1.0",
"line": "8",
"stationName": "whateverrandomname",
"direction": 2
}],
"station67": [{
"departureTime": 1573,
"delay": "-1.0",
"line": "8",
"stationName": "rndmname",
"direction": 2
}],
"station157": [{
"departureTime": 1842,
"delay": "-2.0",
"line": "8",
"stationName": "randomname",
"direction": 2
}]
}}
Here is the working code:
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import com.google.gson.Gson;
public class GSONTest {
public static void main(String[] args){
String gsonStr = "{\"stationsInResponse\": { \"station62\":[ { \"departureTime\":1982,\"delay\":\"-1.0\",\"line\":\"6\",\"stationName\":\"randomname\",\"direction\":2} ],\"station63\":[ { \"departureTime\":1981,\"delay\":\"-1.1\",\"line\":\"7\",\"stationName\":\"randomname2\",\"direction\":3} ]}}";
Gson gson = new Gson();
Response response = gson.fromJson(gsonStr, Response.class);
System.out.println("Map size:"+response.getStationsInResponse().size());
for (Iterator iterator = response.getStationsInResponse().keySet().iterator(); iterator.hasNext();) {
String key = (String) iterator.next();
ArrayList<Station> stationList = (ArrayList<Station>) response.getStationsInResponse().get(key);
for (Iterator iterator2 = stationList.iterator(); iterator2.hasNext();) {
Station station = (Station) iterator2.next();
System.out.println("Delay: "+station.getDelay());
System.out.println("DepartureTime: "+station.getDepartureTime());
System.out.println("Line: "+station.getLine());
System.out.println("StationName: "+station.getStationName());
}
}
}
}
class Response {
private Map<String, List<Station>> stationsInResponse;
//getters and setters
public Map<String, List<Station>> getStationsInResponse() {
return stationsInResponse;
}
public void setStationsInResponse(Map<String, List<Station>> stationsInResponse) {
this.stationsInResponse = stationsInResponse;
}
}
class Station {
private String departureTime;
public String getDepartureTime() {
return departureTime;
}
public void setDepartureTime(String departureTime) {
this.departureTime = departureTime;
}
public String getDelay() {
return delay;
}
public void setDelay(String delay) {
this.delay = delay;
}
public String getLine() {
return line;
}
public void setLine(String line) {
this.line = line;
}
public String getStationName() {
return stationName;
}
public void setStationName(String stationName) {
this.stationName = stationName;
}
public String getDirection() {
return direction;
}
public void setDirection(String direction) {
this.direction = direction;
}
private String delay;
private String line;
private String stationName;
private String direction;
}
Output in console is like this(as I shortened your json string):
Map size:2
Delay: -1.0
DepartureTime: 1982
Line: 6
StationName: randomname
Delay: -1.1
DepartureTime: 1981
Line: 7
StationName: randomname2
First I'll point out your mistake, then I'll give you the solution.
The structure you're asking for in your deserialization code looks like this:
{
"stationsInResponse": {
"station1": [
{
"name": "Station 1"
}
],
"station2": [
{
"name": "Station 2"
}
]
}
}
Solution
The deserialization code you really need to deserialize the structure you're getting as input, is as follows:
Gson gson = new Gson();
Type rootType = new TypeToken<Map<String, List<Station>>>(){}.getType();
Map<String, List<Station>> stationsMap = gson.fromJson(json, rootType);
This is because a JSON object, whose properties are unknown at compile-time, which your root object is, maps to a Java Map<String, ?>. The ? represent the expected Java type of the JSON object value, which in your case is either List<Station> or Station[], whichever you prefer.
If you wanted to, you could combine all the stations in the map into one List of stations like so:
List<Station> stations = new ArrayList<>(stationsMap.size());
for (List<Station> stationsList : stationsMap.values()) {
for (Station station : stationsList) {
stations.add(station);
}
}