1. Introduction to Gson
We have already met Jackson and seen why it is considered the de facto standard for working with JSON in Java. But there is another library that has gained huge popularity, especially in the Android world — Gson. Gson was created at Google as a lightweight and simple solution for serializing and deserializing Java objects to JSON. It is valued for its low barrier to entry: to get started, you hardly need any configuration — most tasks are handled right “out of the box”.
Another advantage of Gson is its lightness. The library takes little space and does not pull in lots of dependencies, so it is often used where app size is critical, for example on mobile devices. Gson became a practical standard for Android projects — compactness and simplicity are crucial there.
By the way, the name Gson stands for Google JSON. Sometimes in the community you can see a joking backronym — Genius’ Son (“son of a genius”), but that is, of course, unofficial. Just a play on words.
Adding Gson to a project
If you use Maven or Gradle, just add the dependency (the version may differ):
Maven:
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.10.1</version>
</dependency>
Gradle:
implementation 'com.google.code.gson:gson:2.10.1'
We have not studied build tools yet, so to begin you can simply download the jar file from the official Gson page and add it to your project.
2. Basic operations: serialization and deserialization
Let’s see how to serialize and deserialize objects with Gson using a simple class.
Example: User class
// Example class
public class User {
private String name;
private int age;
private boolean active;
// Constructor
public User(String name, int age, boolean active) {
this.name = name;
this.age = age;
this.active = active;
}
// Getters and setters (Gson uses them when needed)
public String getName() { return name; }
public int getAge() { return age; }
public boolean isActive() { return active; }
}
Serialization: object → JSON
import com.google.gson.Gson;
public class GsonExample {
public static void main(String[] args) {
User user = new User("Alice", 25, true);
Gson gson = new Gson();
String json = gson.toJson(user);
System.out.println(json);
// {"name":"Alice","age":25,"active":true}
}
}
Note: fields are serialized using their names from the class!
Deserialization: JSON → object
public class GsonExample {
public static void main(String[] args) {
String json = "{\"name\":\"Bob\",\"age\":30,\"active\":false}";
Gson gson = new Gson();
User user = gson.fromJson(json, User.class);
System.out.println(user.getName()); // Bob
System.out.println(user.getAge()); // 30
System.out.println(user.isActive());// false
}
}
Working with lists of objects
Gson is a bit trickier with collections than Jackson, but it’s all solvable.
import java.util.List;
import java.util.Arrays;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
public class GsonListExample {
public static void main(String[] args) {
List<User> users = Arrays.asList(
new User("Alice", 25, true),
new User("Bob", 30, false)
);
Gson gson = new Gson();
String json = gson.toJson(users);
System.out.println(json);
// [{"name":"Alice","age":25,"active":true},{"name":"Bob","age":30,"active":false}]
// List deserialization
Type userListType = new TypeToken<List<User>>(){}.getType();
List<User> users2 = gson.fromJson(json, userListType);
System.out.println(users2.get(0).getName()); // Alice
}
}
Important point: use TypeToken<> to deserialize collections!
3. Configuring Gson: GsonBuilder
Gson provides flexible configuration via GsonBuilder. With it you can enable pretty printing, serialize null, set date formats, and much more.
Example: pretty printing and null serialization
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
public class GsonBuilderExample {
public static void main(String[] args) {
User user = new User("Charlie", 0, false);
Gson gson = new GsonBuilder()
.setPrettyPrinting() // Pretty printing (indents)
.serializeNulls() // Serialize null fields
.create();
String json = gson.toJson(user);
System.out.println(json);
/*
{
"name": "Charlie",
"age": 0,
"active": false
}
*/
}
}
Date formatting
If you have fields of type Date, by default Gson serializes them in a specific format. You can set your own format:
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.util.Date;
public class DateExample {
private String event;
private Date date;
public DateExample(String event, Date date) {
this.event = event;
this.date = date;
}
}
public class Main {
public static void main(String[] args) {
DateExample meeting = new DateExample("Team Meeting", new Date());
Gson gson = new GsonBuilder()
.setDateFormat("yyyy-MM-dd HH:mm:ss")
.create();
String json = gson.toJson(meeting);
System.out.println(json);
// {"event":"Team Meeting","date":"2024-06-10 13:45:23"}
}
}
4. Gson annotations: controlling serialization
Gson supports annotations for more precise control over serialization and deserialization.
@SerializedName — renaming a field
If you want a field to have a different name in JSON, use @SerializedName:
import com.google.gson.annotations.SerializedName;
public class User {
@SerializedName("full_name")
private String name;
private int age;
private boolean active;
public User(String name, int age, boolean active) {
this.name = name;
this.age = age;
this.active = active;
}
}
Now during serialization the field will be called full_name:
User user = new User("Diana", 28, true);
String json = new Gson().toJson(user);
// {"full_name":"Diana","age":28,"active":true}
@Expose — serialize only annotated fields
If you want to serialize only specific fields, use @Expose and configure Gson:
import com.google.gson.annotations.Expose;
public class User {
@Expose
private String name;
@Expose
private int age;
private boolean active; // not serialized
public User(String name, int age, boolean active) {
this.name = name;
this.age = age;
this.active = active;
}
}
Create Gson with @Expose support:
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
Gson gson = new GsonBuilder()
.excludeFieldsWithoutExposeAnnotation()
.create();
User user = new User("Eve", 21, false);
String json = gson.toJson(user);
// {"name":"Eve","age":21}
@Since/@Until — conditional serialization by version
You can use @Since and @Until to serialize fields only for specific versions (rarely used in practice, but good to know).
5. Gson features and limitations
Working with nested objects
Gson works great with nested objects:
public class Profile {
private User user;
private String bio;
public Profile(User user, String bio) {
this.user = user;
this.bio = bio;
}
}
Profile profile = new Profile(new User("Frank", 27, true), "Java developer");
String json = new Gson().toJson(profile);
// {"user":{"name":"Frank","age":27,"active":true},"bio":"Java developer"}
Working with collections
There are no issues with serializing collections (List, Map), but for deserialization use TypeToken (see above).
Gson limitations compared to Jackson
- No support for Java record classes (until recent versions)
- Limited support for the newer date/time API (e.g., LocalDate, LocalDateTime — custom adapters required)
- Cannot work with Jackson annotations
- No support for complex polymorphic structures “out of the box”
- No automatic support for bidirectional references (cyclic)
Custom adapters (TypeAdapter)
If the standard capabilities are not enough, you can write your own adapter for serializing/deserializing complex types.
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import java.io.IOException;
public class BooleanAsIntAdapter extends TypeAdapter<Boolean> {
@Override
public void write(JsonWriter out, Boolean value) throws IOException {
out.value(value ? 1 : 0);
}
@Override
public Boolean read(JsonReader in) throws IOException {
return in.nextInt() == 1;
}
}
// Usage:
Gson gson = new GsonBuilder()
.registerTypeAdapter(Boolean.class, new BooleanAsIntAdapter())
.create();
6. Practice: serialization and deserialization with configuration
Let’s extend your training app and add saving and loading a list of users in JSON format.
User class with annotations
import com.google.gson.annotations.SerializedName;
import com.google.gson.annotations.Expose;
public class User {
@Expose
@SerializedName("full_name")
private String name;
@Expose
private int age;
private boolean active; // not serialized
public User(String name, int age, boolean active) {
this.name = name;
this.age = age;
this.active = active;
}
// getters, setters...
}
Saving a list of users to JSON
import java.util.List;
import java.util.Arrays;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
public class SaveUsers {
public static void main(String[] args) {
List<User> users = Arrays.asList(
new User("Ivan", 23, true),
new User("Olga", 19, false)
);
Gson gson = new GsonBuilder()
.excludeFieldsWithoutExposeAnnotation()
.setPrettyPrinting()
.create();
String json = gson.toJson(users);
System.out.println(json);
/*
[
{
"full_name": "Ivan",
"age": 23
},
{
"full_name": "Olga",
"age": 19
}
]
*/
}
}
Loading a list of users from JSON
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.List;
public class LoadUsers {
public static void main(String[] args) {
String json = "[{\"full_name\":\"Ivan\",\"age\":23},{\"full_name\":\"Olga\",\"age\":19}]";
Gson gson = new GsonBuilder()
.excludeFieldsWithoutExposeAnnotation()
.create();
Type userListType = new TypeToken<List<User>>(){}.getType();
List<User> users = gson.fromJson(json, userListType);
for (User user : users) {
System.out.println(user.getName() + " (" + user.getAge() + ")");
}
// Ivan (23)
// Olga (19)
}
}
7. Comparing Gson and Jackson
| Characteristic | Gson | Jackson |
|---|---|---|
| Ease of use | +++++ (very simple) | +++ (slightly more complex) |
| Library size | Small | Larger |
| Speed | Fast, but slightly slower | Very fast |
| Flexibility | Medium | High (more configuration) |
| Annotation support | Own (@SerializedName) | Own (@JsonProperty and others) |
| Support for new types | Limited | Excellent (Java 8+, record) |
| Android support | Excellent | Good, but heavier |
| Date handling | Only via adapters | Out of the box |
| Polymorphism | Limited | Highly configurable |
8. Common mistakes when working with Gson
Mistake #1: Not using TypeToken for collections.
If you deserialize a list or a map, be sure to use TypeToken<>, otherwise you may get strange errors or empty collections.
Mistake #2: No no-args constructor.
Gson can work without a default constructor, but sometimes when deserializing complex objects without such a constructor, errors can occur. It’s better to always add a no-args constructor if you plan to deserialize.
Mistake #3: Field name mismatch.
If in JSON the field is called "full_name", and in the class it’s "name", without the annotation @SerializedName("full_name") the field won’t be bound and the value will be null.
Mistake #4: Problems with private fields.
Gson can serialize private fields, but if there are only private fields and no getters/setters, issues can sometimes arise during deserialization. It’s better to use getters and setters.
Mistake #5: Working with dates.
By default Gson serializes Date in an inconvenient format. For LocalDate, LocalDateTime and other newer types, you will get serialization errors without custom adapters.
Mistake #6: Not using @Expose while enabling excludeFieldsWithoutExposeAnnotation().
If you enable excludeFieldsWithoutExposeAnnotation() but don’t annotate fields with @Expose, they won’t be serialized or deserialized — the result will be empty JSON or objects with null.
GO TO FULL VERSION