In the Gson blog posts, we've explored the basic functionality. In this blog post, we'll move more towards more realistic data and look at nested objects. You'll learn how easy it is to work with objects, which includes additional complex data.
This is only one post in our comprehensive Gson series. If you're interested in other topics, check out our series outline:
Gson Series Overview
- Mapping of Enums
- Mapping of Circular References
- Generics
- Custom Serialization for Simplification (Part 1)
- Changing the Default Serialization with Custom Serialization (Part 2)
- Custom Deserialization Basics
- Custom Instance Creator
- Customizing (De)Serialization via @JsonAdapter
- Custom Deserialization for Calculated Fields
- On-The-Fly-Parsing With Streams
- ProGuard Configuration
Nested Objects Serialization
We like to demonstrate functionality with hands-on examples, so let's extend our UserSimple
model. In the previous posts, the user model had only a few standard Java types:
public class UserSimple {
String name;
String email;
boolean isDeveloper;
int age;
}
Now our user also has an home address, which has its own model class UserAddress
:
public class UserNested {
String name;
String email;
boolean isDeveloper;
int age;
// new, see below!
UserAddress userAddress;
}
public class UserAddress {
String street;
String houseNumber;
String city;
String country;
}
In other words the user, which is now represented in the UserNested
model, has an additional one-to-one relationship with an address object. The address is represented in the UserAddress
model.
In Java the two models can be cleanly separated by classes and we keep the reference via the UserAddress userAddress
field. However, in JSON we don't have classes or references. The only way in JSON (without working with IDs and binding things together later) is to nest the user address in the user object. Basically, in JSON we just create a new object with {}
after the field name:
{
"age": 26,
"email": "norman@futurestud.io",
"isDeveloper": true,
"name": "Norman",
"userAddress": {
"city": "Magdeburg",
"country": "Germany",
"houseNumber": "42A",
"street": "Main Street"
}
}
Unlike the other properties (age
, email
, ...) the new userAddress
doesn't have a direct value. Instead, it contains sub values, which are wrapped in {}
. It's important that you understand the brackets after the field name right away as a signal this-is-a-nested-object.
Enough theory. It's time to see what Gson creates from a UserNested
object. You probably will recognize the pattern. Gson doesn't require any configuration. It'll automatically infer the structure of the data based on the passed classes:
UserAddress userAddress = new UserAddress(
"Main Street",
"42A",
"Magdeburg",
"Germany"
);
UserNested userObject = new UserNested(
"Norman",
"norman@futurestud.io",
26,
true,
userAddress
);
Gson gson = new Gson();
String userWithAddressJson = gson.toJson(userObject);
The value of the userWithAddressJson
string is interesting:
{
"age": 26,
"email": "norman@futurestud.io",
"isDeveloper": true,
"name": "Norman",
"userAddress": {
"city": "Magdeburg",
"country": "Germany",
"houseNumber": "42A",
"street": "Main Street"
}
}
Yes, Gson sorted the fields alphabetically again, but otherwise the result is exactly as we've expected. Gson correctly created the nested userAddress
JSON object. Of course, we could add more nested objects for the user's payment method or work address. Even the nested objects can have nested objects!
In the next part, we'll look at the other direction. How can we deserialize the complex, nested JSON into Java objects?
Nested Objects Deserialization
In the previous section we assumed the models are already there and we just want to create a matching JSON. Especially for the app developers in the real-world, it's often the other way around. The API is returning some JSON and we need to model the classes for that JSON.
If you've read the first few paragraphs of this blog post, you already have a feeling on how to create the model classes. We don't want you to get bored, so we're going to move away from the user examples to a nice little restaurant.
{
"name": "Future Studio Steak House",
"owner": {
"name": "Christian",
"address": {
"city": "Magdeburg",
"country": "Germany",
"houseNumber": "42A",
"street": "Main Street"
}
},
"cook": {
"age": 18,
"name": "Marcus",
"salary": 1500
},
"waiter": {
"age": 18,
"name": "Norman",
"salary": 1000
}
}
This comes from our API and we want to utilize Gson to automatically create the matching Java objects for it. First, you need to model the base class, which all top-level fields live in:
public class Restaurant {
String name;
Owner owner;
Cook cook;
Waiter waiter;
}
See how we created a String for the name
and extra Java classes for the other three? Some of you might have come to a different result. Creating the Java objects is not always unambiguous. For example, based on the JSON, we see that the cook
and waiter
nested objects have the same structure. You could still create a different class, like we did above, or make a general Staff
class for both:
public class Restaurant {
String name;
Owner owner;
Staff cook;
Staff waiter;
}
Either one would work. If in doubt, we usually tend to make an extra class to avoid future complications. For example, if the cook model changes, but the waiter model stays the same you might have to change a bunch of code. Thus, we'll throw away the Staff
solution for now. Of course, we still have to create the Java model classes for the second level objects:
public class Owner {
String name;
UserAddress address;
}
public class Cook {
String name;
int age;
int salary;
}
public class Waiter {
String name;
int age;
int salary;
}
Alright, we cheated a bit and re-used the UserAddress
from the first part. But only because it matched perfectly ;-)
Nevertheless, we hope you understand the process of creating Java model classes from a JSON string. You'll need to go from top-level to the deepest level until your nested JSON only has regular types left.
The main work is done and we can throw everything to Gson. Of course, if we've done our work correctly, it'll handle everything gracefully and create the Java objects with just a few lines:
String restaurantJson = "{ 'name':'Future Studio Steak House', 'owner':{ 'name':'Christian', 'address':{ 'city':'Magdeburg', 'country':'Germany', 'houseNumber':'42', 'street':'Main Street'}},'cook':{ 'age':18, 'name': 'Marcus', 'salary': 1500 }, 'waiter':{ 'age':18, 'name': 'Norman', 'salary': 1000}}";
Gson gson = new Gson();
Restaurant restaurantObject = gson.fromJson(restaurantJson, Restaurant.class);
The restaurantObject
actually contains all the information, which are in the JSON:
Hint: Creating Java model classes from JSONs can be tedious work. After you've grasped the concepts you might want to use tools which automate the process. We like jsonschema2pojo.org quite a bit.
Outlook
In this blog post, you've learned how Gson deals with nested objects. Serializing complex data types is no problem for Gson. Even deserializing is no big deal, but you've to do some (possibly automated) work in creating the appropriate model classes first.
If you've feedback or a question, let us know in the comments or on twitter @futurestud_io.
Make it rock & enjoy coding!