Gson — Mapping of Arrays and Lists of Objects

Datetime:2016-08-23 03:11:24          Topic:          Share

Welcome back to another blog post in our Gson series. After reviewing the basics of Gson, model annotations and mapping of nested objects, we'll go on to a core feature: mapping of Arrays and Lists. Almost every data model out there utilizes some form of list. Fortunately, Gson makes it really easy to deal with them.

As you know, we're publishing a whole series about Gson. If you're interested in another topic, check out our series outline:

Gson Series Overview

  1. Getting Started with Java-JSON Serialization & Deserialization
  2. Mapping of Nested Objects
  3. Mapping of Arrays and Lists of Objects
  4. Mapping of Maps
  5. Mapping of Sets
  6. Mapping of Null Values
  7. Gson Model Annotations — How to Ignore Fields with @Expose
  8. Gson Model Annotations — How to Change the Naming of Fields with @SerializedName

Difference Between Arrays and Lists

Before we go into specific (de)serialization examples, we want to examine the two Java structures Arrays and Lists. The Java implementation is significantly different and either one has its advantages. What you're going to apply in your use case depends on the software requirements and at least partly your personal taste. The interesting thing when it comes to mapping list or array structures to JSON: it doesn't matter.

In the JSON data format, there are no lists or arrays. Yes, the Java implementations make a huge difference between them, but on a high level they represent the exact same data in a list form. In the rest of the blog post, we'll name them object lists , but on the Java side they can be either. If this is a bit confusing, don't worry, it'll be much clearer after a few examples.

Data Serialization of Arrays/Lists

Remember our restaurant model from theprevious blog post about nested objects? It's time that the restaurant also includes a menu, isn't it? We want to know what yummy foods are offered for which prices. A restaurant menu can be structured as a list of menu items. One item is exactly one option a customer can order. In our simple restaurant, each item has a description and a price.

Hint: we'll keep it easy and not deal with menu categories, combos or side dishes. It's obviously not a complete model, so don't use it for your commercial Restaurant app …

If we think about the concrete Java model implementation, we'll get something close to this:

public class RestaurantWithMenu {  
    String name;

    List<RestaurantMenuItem> menu;
    //RestaurantMenuItem[] menu; // alternative, either one is fine
}

public class RestaurantMenuItem {  
    String description;
    float price;
}

Just withnested objects the way Java handles the objects is different than JSON is able to. Java can keep them as separate classes and just hold a reference to the List or Array implementation. JSON needs to keep the lists as a local, nested lists. That means on a high level we would expect on the JSON side something like this:

{
  "name": "Future Studio Steak House",
  "menu": [
    ...
  ]
}

Similar to nested objects, we don't have a direct value for menu . Instead, JSON declares that a list of objects is coming by wrapping the value with [] . As mentioned above, there is no difference if this is an array or a list. In the JSON data structure it looks identical.

The content of the menu are a bunch of objects. In our case, they're the restaurant's menu items. Let's run Gson to see how a complete JSON would look like.

We hope you know the drill by now. Get your Java objects, initialize Gson and then let Gson create the matching JSON:

List<RestaurantMenuItem> menu = new ArrayList<>();  
menu.add(new RestaurantMenuItem("Spaghetti", 7.99f));  
menu.add(new RestaurantMenuItem("Steak", 12.99f));  
menu.add(new RestaurantMenuItem("Salad", 5.99f));

RestaurantWithMenu restaurant =  
        new RestaurantWithMenu("Future Studio Steak House", menu);

Gson gson = new Gson();  
String restaurantJson = gson.toJson(restaurant);  

The restaurantJson contains the following content:

{
  "menu": [
    {
      "description": "Spaghetti",
      "price": 7.99
    },
    {
      "description": "Steak",
      "price": 12.99
    },
    {
      "description": "Salad",
      "price": 5.99
    }
  ],
  "name": "Future Studio Steak House"
}

As always, the order is a little surprising, but the list comes first since the menu is alphabetically before the name . Besides the order, everything else looks like expected. The list (indicated by […] ) contains multiple objects (indicated each by {…} ).

But we're not always sending lists of data nested in a single object, like we've done above. Sometimes we just want to send a list. Of course, Gson also supports the JSON serialization of lists. For example, if we would just serialize the menu items like this:

List<RestaurantMenuItem> menu = new ArrayList<>();  
menu.add(new RestaurantMenuItem("Spaghetti", 7.99f));  
menu.add(new RestaurantMenuItem("Steak", 12.99f));  
menu.add(new RestaurantMenuItem("Salad", 5.99f));

Gson gson = new Gson();  
String menuJson = gson.toJson(menu);  

This would lead to:

[
  {
    "description": "Spaghetti",
    "price": 7.99
  },
  {
    "description": "Steak",
    "price": 12.99
  },
  {
    "description": "Salad",
    "price": 5.99
  }
]

Let us point out the critical difference: the first character of the JSON is a [ , which indicates that a list of objects is coming! So far, we've only looked at objects , which start with a { . You should try to memorize that difference right away. You'll need it all the time and right away in the next section, if you keep reading.

Data Deserialization of Arrays/Lists

In this second part we'll go through the deserialization. In other words how we can use Gson to map from lists in a JSON structure to Java objects. In the previous example, we've looked at the important difference if the list is the root or nested in an object in the JSON data.

Lists as Part of an Object

So let's do an exercise. In case we at Future Studio will start our own API, we'll also provide an endpoint /founders . This endpoint would return the three of us with our name and a flowerCount , which displays the amount of plants on our desk. So has the following JSON a list as root?

[
    {
      "name": "Christian",
      "flowerCount": 1
    },
    {
      "name": "Marcus",
      "flowerCount": 3
    },
    {
      "name": "Norman",
      "flowerCount": 2
    }
]

Yes, you're correct. The imaginary /getFounders endpoint returns a list directly. The JSON starts and ends with a [] . You're also correct that Marcus likes to work at a green jungle of a desk.

So how do we map this with Gson to Java objects? The first step is to create our model:

public class Founder {  
    String name;
    int flowerCount;
}

The second step depends on you. Do you want to use Lists or Arrays as your type?

Arrays

If you want to use Arrays, it's pretty simple. You can directly use the fromJson function as we've done before and pass the model class as an array like gson.fromJson(founderGson, Founder[].class); .

String founderJson = "[{'name': 'Christian','flowerCount': 1}, {'name': 'Marcus', 'flowerCount': 3}, {'name': 'Norman', 'flowerCount': 2}]";

Gson gson = new Gson();  
Founder[] founderArray = gson.fromJson(founderJson, Founder[].class);  

This will create a Java array of founder objects, which have the attributes mapped correctly:

Lists

A lot of developers nowadays prefer Java Lists due to the wider range of methods. Unfortunately, you cannot directly pass a List<Founder> to Gson. In order for Gson to understand the List structure correctly, you've to figure out its Type . Luckily, there is a Gson class TypeToken to assist you in finding the correct Type for pretty much any class configuration. For our Founder class in an ArrayList , it'd look like this:

Type founderListType = new TypeToken<ArrayList<Founder>>(){}.getType();  

You can use the result of the statement as the type for the Gson call:

String founderJson = "[{'name': 'Christian','flowerCount': 1}, {'name': 'Marcus', 'flowerCount': 3}, {'name': 'Norman', 'flowerCount': 2}]";

Gson gson = new Gson();

Type founderListType = new TypeToken<ArrayList<Founder>>(){}.getType();

List<Founder> founderList = gson.fromJson(founderJson, founderListType);  

This works just as well as the Array approach:

In the end it's up to your personal preference and the use case if you map your data to an Array or a List. Let's look at one more topic: how to deserialize a list when it's nested in an object:

Lists as Root Object

We've extended our imaginary Future Studio API with another, general endpoint /getInfo . It returns more than just the founder information:

{
  "name": "Future Studio Dev Team",
  "website": "https://futurestud.io",
  "founders": [
    {
      "name": "Christian",
      "flowerCount": 1
    },
    {
      "name": "Marcus",
      "flowerCount": 3
    },
    {
      "name": "Norman",
      "flowerCount": 2
    }
  ]
}

We hope you know the drill now. First, we've to write an appropriate model for the JSON response. We can re-use our Founder class from the previous section:

public class GeneralInfo {  
    String name;
    String website;
    List<Founder> founders;
}

The advantage of the list nested in an object is the simple Gson call without any TypeToken handling. We can directly pass the class:

String generalInfoJson = "{'name': 'Future Studio Dev Team', 'website': 'https://futurestud.io', 'founders': [{'name': 'Christian', 'flowerCount': 1 }, {'name': 'Marcus','flowerCount': 3 }, {'name': 'Norman','flowerCount': 2 }]}";

Gson gson = new Gson();

GeneralInfo generalInfoObject = gson.fromJson(generalInfoJson, GeneralInfo.class);  

This will lead to a complete object:

Of course, you could change the GeneralInfo class to use Founder[] array instead of List<Founder> . The Gson call would stay the same though.

Note: did you notice that Gson has no problems with both, the GeneralInfo and the Founder model having a name property. It'll serialize and deserialize it without any problems. Awesome!

Lists Nested in Lists

If you're wondering, Gson has no problems with Lists nested in Lists. For example, the following model would be no problem:

public class GeneralInfo {  
    String name;
    String website;
    List<FounderWithPets> founders;
}

The FounderWithPets class is now a little extended with a list of pets:

public class FounderWithPets {  
    String name;
    int flowerCount;
    List<Pet> pets;
}

The Pet class on the other hand includes a list of toys:

public class Pet {  
    String name;
    List<Toy> toys;
}

The Toy class includes … okay, we stop now. We hope this brings the point across: you can wrap lists in lists (even more than once) without any issues. Gson handles the serialization and deserialization like a champ.

Outlook

In this blog post you've learned how Gson is able to map List (or Array) data without any problems. Gson is flexible and accepts List and Array implementations on the Java side. You need to learn how to recognize from the JSON structure if the list is the root or nested in an object. You also learned how to set up a different deserialization from JSON to a Java Array or a Java List.

If you've feedback or a question, let us know in the comments or on twitter @futurestud_io .

Make it rock & enjoy coding!