Type Token

import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;

Problem That TypeToken solves

// Consider these two lists
List<String> stringList = new ArrayList<>();
List<Integer> integerList = new ArrayList<>();

// At runtime, due to type erasure, Java sees both as just List
// This means we can't directly tell Gson what type to deserialize to

// with the help of TypeToken we tell Gson about type of data in process of ser and desr.

// Creating a TypeToken for a List of Strings
Type listType = new TypeToken<List<String>>(){}.getType();

// Using it with Gson
List<String> strings = gson.fromJson(jsonString, listType);

Different Ways to Use TypeToken

  1. Simple Generic Types

// For List<String>
Type stringListType = new TypeToken<List<String>>(){}.getType();

// For Set<Integer>
Type integerSetType = new TypeToken<Set<Integer>>(){}.getType();

// For Map<String, Boolean>
Type mapType = new TypeToken<Map<String, Boolean>>(){}.getType();
  1. Nested Generic Types

// For List<List<String>>
Type nestedListType = new TypeToken<List<List<String>>>(){}.getType();

// For Map<String, List<User>>
Type complexMapType = new TypeToken<Map<String, List<User>>>(){}.getType();
  1. Custom Generic Classes

// Generic response wrapper
public class ApiResponse<T> {
    private int status;
    private T data;
    // getters and setters
}

// Usage
Type responseType = new TypeToken<ApiResponse<User>>(){}.getType();
ApiResponse<User> response = gson.fromJson(json, responseType);

Creating Reusable TypeTokens

public class TypeTokens {
    // Private constructor to prevent instantiation
    private TypeTokens() {}
    
    // Reusable TypeToken for List<String>
    public static final Type STRING_LIST_TYPE = new TypeToken<List<String>>(){}.getType();
    
    // Reusable TypeToken for Map<String, User>
    public static final Type STRING_USER_MAP_TYPE = new TypeToken<Map<String, User>>(){}.getType();
}

// Usage
List<String> strings = gson.fromJson(json, TypeTokens.STRING_LIST_TYPE);

Advanced Usage with Parameterized Types

public class GenericTypeHandler<T> {
    private final Type type;

    public GenericTypeHandler() {
        // Capture the actual type parameter
        Type superclass = getClass().getGenericSuperclass();
        if (superclass instanceof ParameterizedType) {
            this.type = ((ParameterizedType) superclass).getActualTypeArguments()[0];
        } else {
            throw new RuntimeException("Missing type parameter.");
        }
    }

    public T fromJson(String json) {
        return new Gson().fromJson(json, type);
    }
}

// Usage
public class UserListHandler extends GenericTypeHandler<List<User>> {}

UserListHandler handler = new UserListHandler();
List<User> users = handler.fromJson(jsonString);

Working with Complex Nested Structures

public class ComplexTypeExample {
    public static void main(String[] args) {
        Gson gson = new Gson();
        
        // Complex nested structure
        Type complexType = new TypeToken<Map<String, List<Set<User>>>>(){}.getType();
        
        // Creating sample data
        Map<String, List<Set<User>>> data = new HashMap<>();
        // ... populate data ...
        
        // Serialization
        String json = gson.toJson(data, complexType);
        
        // Deserialization
        Map<String, List<Set<User>>> parsed = gson.fromJson(json, complexType);
    }
}

Some points to consider -

  1. Insure proper type safety.

  2. Do proper error handling.

  3. Create type tokens and reuse them.

Last updated