Gson

SerializationJavaJSONGoogleObject MappingReflectionAnnotations

Library

Gson

Overview

Gson is a high-performance JSON library for Java developed by Google. It enables seamless conversion between Java objects and JSON with a concise API and is widely used as the standard in many Java frameworks including Spring Boot. With flexible annotations, custom adapters, and diverse configuration options, it handles complex enterprise-level scenarios. Currently in maintenance mode, it continues to receive bug fixes and minor updates while remaining broadly trusted throughout the Java ecosystem.

Details

Gson 2.13.1 is a mature library with over 10 years of development history. As of 2025, it has entered maintenance mode, but existing bug fixes continue to be applied, providing stable JSON serialization functionality. It features a developer-friendly design with simple toJson/fromJSON methods, rich annotations (@SerializedName, @JsonIgnore, etc.), TypeToken support for generics, and custom serializers/deserializers. However, caution is advised when using it on Android platforms.

Key Features

  • Simple API: Intuitive toJson() and fromJson() methods
  • Rich Annotations: Fine-grained control with @SerializedName, @Expose, @Since, etc.
  • Generics Support: Type-safe collection processing via TypeToken
  • Customization: Flexible conversion through custom serializers/deserializers
  • Field Exclusion: Multiple exclusion strategies (modifiers, annotations, versioning)
  • Wide Adoption: Standard usage in many frameworks including Spring Boot

Pros and Cons

Pros

  • Low learning curve with intuitive API for easy Java object-JSON conversion
  • Fine-grained configuration control through rich annotations and GsonBuilder
  • Safe generic type processing via TypeToken
  • Support for complex conversion logic through custom serializers/deserializers
  • Enterprise-level reliability backed by over 10 years of track record
  • Open source and free with active community support

Cons

  • No new feature additions expected due to maintenance mode status
  • Not recommended for Android use; Kotlin Serialization or Moshi are preferred alternatives
  • Poor compatibility with obfuscation/optimization due to reflection-based approach
  • Limited support for JVM language-specific features (Kotlin, Scala)
  • May lag behind Jackson and other modern libraries in performance
  • Configuration can become complex for intricate JSON schemas

Reference Pages

Code Examples

Basic Setup

<!-- Maven Dependencies -->
<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.13.1</version>
</dependency>
// Gradle Dependencies
dependencies {
    implementation 'com.google.code.gson:gson:2.13.1'
}

Basic Serialization and Deserialization

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import java.util.List;
import java.util.Arrays;

// Basic POJO
class User {
    private int id;
    private String name;
    private String email;
    private boolean active;
    
    // Constructor
    public User(int id, String name, String email, boolean active) {
        this.id = id;
        this.name = name;
        this.email = email;
        this.active = active;
    }
    
    // Default constructor (for deserialization)
    public User() {}
    
    // getter/setter
    public int getId() { return id; }
    public void setId(int id) { this.id = id; }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }
    public boolean isActive() { return active; }
    public void setActive(boolean active) { this.active = active; }
}

public class GsonBasicExample {
    public static void main(String[] args) {
        Gson gson = new Gson();
        
        // Object creation
        User user = new User(1, "John Doe", "[email protected]", true);
        
        // 1. Object → JSON string
        String jsonString = gson.toJson(user);
        System.out.println("JSON string: " + jsonString);
        // Output: {"id":1,"name":"John Doe","email":"[email protected]","active":true}
        
        // 2. JSON string → Object
        String inputJson = "{\"id\":2,\"name\":\"Jane Smith\",\"email\":\"[email protected]\",\"active\":false}";
        User deserializedUser = gson.fromJson(inputJson, User.class);
        System.out.println("Deserialized result: " + deserializedUser.getName());
        
        // 3. Array processing
        int[] numbers = {1, 2, 3, 4, 5};
        String numbersJson = gson.toJson(numbers);
        System.out.println("Array JSON: " + numbersJson);
        
        int[] deserializedNumbers = gson.fromJson("[10,20,30]", int[].class);
        System.out.println("Array deserialized: " + Arrays.toString(deserializedNumbers));
        
        // 4. Collection processing (using TypeToken)
        List<User> users = Arrays.asList(
            new User(1, "User 1", "[email protected]", true),
            new User(2, "User 2", "[email protected]", false)
        );
        
        String usersJson = gson.toJson(users);
        System.out.println("User list JSON: " + usersJson);
        
        // Deserialize List<User> using TypeToken
        TypeToken<List<User>> userListType = new TypeToken<List<User>>(){};
        List<User> deserializedUsers = gson.fromJson(usersJson, userListType.getType());
        System.out.println("Deserialized user count: " + deserializedUsers.size());
        
        // 5. Primitive type processing
        String stringJson = gson.toJson("Hello Gson");
        int intJson = gson.fromJson("42", int.class);
        boolean boolJson = gson.fromJson("true", boolean.class);
        
        System.out.println("String JSON: " + stringJson);
        System.out.println("Integer deserialized: " + intJson);
        System.out.println("Boolean deserialized: " + boolJson);
    }
}

Annotations and Customization

import com.google.gson.annotations.*;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;

// Advanced annotation usage example
public class AdvancedUser {
    
    @SerializedName("user_id") // Specify JSON property name
    private int id;
    
    @SerializedName("full_name")
    private String name;
    
    @Expose(serialize = true, deserialize = true) // Explicit exposure control
    private String email;
    
    @Expose(serialize = false, deserialize = true) // Write-only
    private String password;
    
    @Expose(serialize = true, deserialize = false) // Read-only
    private String internalToken;
    
    @Since(1.1) // Valid from version 1.1 onwards
    private String newField;
    
    @Until(2.0) // Valid until version 2.0
    private String deprecatedField;
    
    private transient String temporaryData; // Excluded from serialization
    
    @JsonAdapter(LocalDateTimeAdapter.class) // Custom adapter specification
    private LocalDateTime createdAt;
    
    // Constructor
    public AdvancedUser(int id, String name, String email) {
        this.id = id;
        this.name = name;
        this.email = email;
        this.createdAt = LocalDateTime.now();
        this.internalToken = "internal_" + id;
    }
    
    // getter/setter
    public int getId() { return id; }
    public String getName() { return name; }
    public String getEmail() { return email; }
    public void setPassword(String password) { this.password = password; }
    public LocalDateTime getCreatedAt() { return createdAt; }
}

// Custom adapter example
class LocalDateTimeAdapter implements JsonSerializer<LocalDateTime>, JsonDeserializer<LocalDateTime> {
    private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    
    @Override
    public JsonElement serialize(LocalDateTime src, Type typeOfSrc, JsonSerializationContext context) {
        return new JsonPrimitive(src.format(formatter));
    }
    
    @Override
    public LocalDateTime deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) 
            throws JsonParseException {
        return LocalDateTime.parse(json.getAsString(), formatter);
    }
}

public class AnnotationExample {
    public static void main(String[] args) {
        // Basic Gson (processes all fields)
        Gson basicGson = new Gson();
        
        // Custom configured Gson
        Gson customGson = new GsonBuilder()
            .excludeFieldsWithoutExposeAnnotation() // Process only @Expose annotations
            .setVersion(1.5) // Set as version 1.5
            .setPrettyPrinting() // Formatted output
            .serializeNulls() // Output null values
            .create();
        
        AdvancedUser user = new AdvancedUser(1, "Advanced User", "[email protected]");
        user.setPassword("secret123");
        
        // Basic serialization
        String basicJson = basicGson.toJson(user);
        System.out.println("Basic JSON:\n" + basicJson);
        
        // Custom configured serialization
        String customJson = customGson.toJson(user);
        System.out.println("Custom JSON:\n" + customJson);
        
        // Verify annotation control
        System.out.println("Only email and password are output due to @Expose annotations");
        System.out.println("Version control applied through @Since/@Until annotations");
    }
}

Detailed Configuration with GsonBuilder

import com.google.gson.*;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Modifier;
import java.util.Date;
import java.text.DateFormat;

// Custom exclusion strategy implementation
class CustomExclusionStrategy implements ExclusionStrategy {
    
    @Override
    public boolean shouldSkipField(FieldAttributes field) {
        // Exclude fields starting with "internal"
        return field.getName().startsWith("internal");
    }
    
    @Override
    public boolean shouldSkipClass(Class<?> clazz) {
        // Exclude specific classes (e.g., InternalConfig)
        return clazz.getSimpleName().equals("InternalConfig");
    }
}

public class GsonConfigurationExample {
    
    public static Gson createConfiguredGson() {
        return new GsonBuilder()
            // Basic settings
            .setPrettyPrinting() // Formatted output
            .serializeNulls() // Output null values
            .disableHtmlEscaping() // Disable HTML escaping
            
            // Date processing
            .setDateFormat("yyyy-MM-dd HH:mm:ss") // Date format
            .setDateFormat(DateFormat.FULL, DateFormat.FULL) // Locale-aware dates
            
            // Field exclusion settings
            .excludeFieldsWithModifiers(Modifier.STATIC, Modifier.TRANSIENT) // Exclude by modifiers
            .setExclusionStrategies(new CustomExclusionStrategy()) // Custom exclusion strategy
            
            // Naming conventions
            .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) // snake_case
            
            // Version management
            .setVersion(2.0) // Set as version 2.0
            
            // Complex Map key support
            .enableComplexMapKeySerialization()
            
            // Error handling
            .setLenient() // Allow non-strict JSON parsing
            
            .create();
    }
    
    public static void demonstrateConfiguration() {
        Gson gson = createConfiguredGson();
        
        // Test data
        TestData data = new TestData();
        data.name = "Configuration Test";
        data.createdAt = new Date();
        data.internalId = "internal_123"; // Field to be excluded
        data.publicId = "public_456";
        
        // Serialization
        String json = gson.toJson(data);
        System.out.println("Configured JSON:\n" + json);
        
        // Complex Map key example
        Map<ComplexKey, String> complexMap = new HashMap<>();
        complexMap.put(new ComplexKey("key1", 100), "value1");
        complexMap.put(new ComplexKey("key2", 200), "value2");
        
        String complexJson = gson.toJson(complexMap);
        System.out.println("Complex Map key JSON:\n" + complexJson);
        
        // Deserialization
        TestData deserializedData = gson.fromJson(json, TestData.class);
        System.out.println("Deserialized result: " + deserializedData.name);
    }
    
    static class TestData {
        String name;
        Date createdAt;
        String internalId; // Excluded by CustomExclusionStrategy
        String publicId;
        private static String staticField = "static"; // Excluded by modifier
        private transient String transientField = "transient"; // Excluded by modifier
    }
    
    static class ComplexKey {
        String name;
        int value;
        
        ComplexKey(String name, int value) {
            this.name = name;
            this.value = value;
        }
        
        // equals/hashCode implementation required
        @Override
        public boolean equals(Object obj) {
            if (this == obj) return true;
            if (obj == null || getClass() != obj.getClass()) return false;
            ComplexKey that = (ComplexKey) obj;
            return value == that.value && Objects.equals(name, that.name);
        }
        
        @Override
        public int hashCode() {
            return Objects.hash(name, value);
        }
    }
}

JSON Parsing and Manipulation (using JsonElement)

import com.google.gson.*;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import java.io.StringReader;
import java.io.StringWriter;

public class JsonElementExample {
    
    public static void jsonElementOperations() {
        Gson gson = new Gson();
        
        // JSON string
        String jsonString = """
            {
                "user": {
                    "id": 123,
                    "name": "JSON Operations User",
                    "contact": {
                        "email": "[email protected]",
                        "phone": "555-1234"
                    },
                    "hobbies": ["reading", "movies", "programming"]
                },
                "metadata": {
                    "version": "1.0",
                    "timestamp": "2025-01-01T12:00:00Z"
                }
            }
            """;
        
        // Parse as JsonElement
        JsonElement rootElement = JsonParser.parseString(jsonString);
        
        // Read values
        JsonObject root = rootElement.getAsJsonObject();
        JsonObject user = root.getAsJsonObject("user");
        
        int userId = user.get("id").getAsInt();
        String userName = user.get("name").getAsString();
        String email = user.getAsJsonObject("contact").get("email").getAsString();
        
        System.out.println("User information:");
        System.out.println("ID: " + userId);
        System.out.println("Name: " + userName);
        System.out.println("Email: " + email);
        
        // Array processing
        JsonArray hobbies = user.getAsJsonArray("hobbies");
        System.out.println("Hobbies:");
        for (JsonElement hobby : hobbies) {
            System.out.println("  - " + hobby.getAsString());
        }
        
        // Conditional access
        if (root.has("metadata")) {
            JsonObject metadata = root.getAsJsonObject("metadata");
            String version = metadata.get("version").getAsString();
            System.out.println("Version: " + version);
        }
        
        // Null value check
        JsonElement description = user.get("description");
        if (description == null || description.isJsonNull()) {
            System.out.println("Description field does not exist or is null");
        }
    }
    
    public static void jsonElementModification() {
        // Create new JSON object
        JsonObject root = new JsonObject();
        
        // Add primitive values
        root.addProperty("name", "New User");
        root.addProperty("age", 25);
        root.addProperty("active", true);
        root.addProperty("score", 95.5);
        
        // Create nested object
        JsonObject contact = new JsonObject();
        contact.addProperty("email", "[email protected]");
        contact.addProperty("phone", "555-9876");
        root.add("contact", contact);
        
        // Create array
        JsonArray skills = new JsonArray();
        skills.add("Java");
        skills.add("JSON");
        skills.add("Gson");
        root.add("skills", skills);
        
        // Add existing object
        User existingUser = new User(999, "Existing User", "[email protected]", true);
        root.add("existing_user", JsonParser.parseString(new Gson().toJson(existingUser)));
        
        // Output result
        Gson gson = new GsonBuilder().setPrettyPrinting().create();
        String result = gson.toJson(root);
        System.out.println("Created JSON:\n" + result);
        
        // Partial modifications
        root.addProperty("age", 26); // Update age
        root.getAsJsonObject("contact").addProperty("address", "New York"); // Add address
        skills.add("Spring Boot"); // Add skill
        
        System.out.println("Updated JSON:\n" + gson.toJson(root));
    }
    
    public static void streamingJsonProcessing() {
        try {
            // Streaming write
            StringWriter stringWriter = new StringWriter();
            JsonWriter writer = new JsonWriter(stringWriter);
            writer.setIndent("  "); // Set indentation
            
            writer.beginObject();
            writer.name("users").beginArray();
            
            // Streaming processing of large data
            for (int i = 1; i <= 1000; i++) {
                writer.beginObject();
                writer.name("id").value(i);
                writer.name("name").value("User " + i);
                writer.name("active").value(i % 2 == 0);
                writer.name("score").value(i * 0.95);
                writer.endObject();
                
                if (i % 100 == 0) {
                    System.out.println("Processed: " + i + " users");
                }
            }
            
            writer.endArray();
            writer.endObject();
            writer.close();
            
            String jsonResult = stringWriter.toString();
            System.out.println("Streaming processing complete: " + jsonResult.length() + " characters");
            
            // Streaming read
            JsonReader reader = new JsonReader(new StringReader(jsonResult));
            reader.beginObject();
            
            while (reader.hasNext()) {
                String name = reader.nextName();
                if ("users".equals(name)) {
                    reader.beginArray();
                    int count = 0;
                    
                    while (reader.hasNext()) {
                        reader.beginObject();
                        
                        int id = 0;
                        String userName = "";
                        boolean active = false;
                        
                        while (reader.hasNext()) {
                            String fieldName = reader.nextName();
                            switch (fieldName) {
                                case "id":
                                    id = reader.nextInt();
                                    break;
                                case "name":
                                    userName = reader.nextString();
                                    break;
                                case "active":
                                    active = reader.nextBoolean();
                                    break;
                                case "score":
                                    reader.nextDouble(); // Skip
                                    break;
                            }
                        }
                        
                        reader.endObject();
                        count++;
                        
                        if (active && id <= 10) {
                            System.out.println("Active user: " + userName);
                        }
                    }
                    
                    reader.endArray();
                    System.out.println("Streaming read complete: " + count + " users");
                }
            }
            
            reader.endObject();
            reader.close();
            
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    public static void main(String[] args) {
        System.out.println("=== JsonElement Operations Example ===");
        jsonElementOperations();
        
        System.out.println("\n=== JsonElement Modification Example ===");
        jsonElementModification();
        
        System.out.println("\n=== Streaming Processing Example ===");
        streamingJsonProcessing();
    }
}

Error Handling and Best Practices

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

public class GsonErrorHandlingExample {
    
    public static void comprehensiveErrorHandling() {
        Gson gson = new Gson();
        
        // 1. Basic error handling
        try {
            String invalidJson = "{\"name\": John Doe, \"age\": }"; // Invalid JSON
            User user = gson.fromJson(invalidJson, User.class);
        } catch (JsonSyntaxException e) {
            System.out.println("JSON syntax error: " + e.getMessage());
        }
        
        // 2. Type mismatch error
        try {
            String wrongTypeJson = "{\"id\": \"string\", \"name\": \"Test User\"}";
            User user = gson.fromJson(wrongTypeJson, User.class);
        } catch (JsonSyntaxException e) {
            System.out.println("Type conversion error: " + e.getMessage());
        }
        
        // 3. Safe deserialization
        User safeUser = safeDeserialize("{\"id\": 123, \"name\": \"Safe User\"}", User.class);
        if (safeUser != null) {
            System.out.println("Safely deserialized: " + safeUser.getName());
        }
        
        // 4. Partial JSON parsing
        String partialJson = "{\"validField\": \"value\", \"invalidField\": }";
        JsonObject partial = parsePartialJson(partialJson);
        if (partial != null) {
            System.out.println("Partial parse result: " + partial.toString());
        }
        
        // 5. Robust Gson configuration
        Gson robustGson = createRobustGson();
        String flexibleJson = """
            {
                "id": "123",
                "name": "Robust User",
                "email": "[email protected]",
                "unknownField": "This will be ignored"
            }
            """;
        
        User robustUser = safeDeserialize(flexibleJson, User.class, robustGson);
        if (robustUser != null) {
            System.out.println("Robust deserialization: " + robustUser.getName());
        }
    }
    
    // Safe deserialization method
    public static <T> T safeDeserialize(String json, Class<T> clazz) {
        return safeDeserialize(json, clazz, new Gson());
    }
    
    public static <T> T safeDeserialize(String json, Class<T> clazz, Gson gson) {
        try {
            return gson.fromJson(json, clazz);
        } catch (JsonSyntaxException e) {
            System.err.println("JSON syntax error: " + e.getMessage());
            return null;
        } catch (JsonIOException e) {
            System.err.println("JSON IO error: " + e.getMessage());
            return null;
        } catch (JsonParseException e) {
            System.err.println("JSON parse error: " + e.getMessage());
            return null;
        } catch (Exception e) {
            System.err.println("Unexpected error: " + e.getMessage());
            return null;
        }
    }
    
    // Partial JSON parsing
    public static JsonObject parsePartialJson(String json) {
        try {
            return JsonParser.parseString(json).getAsJsonObject();
        } catch (JsonSyntaxException e) {
            System.err.println("Error during partial parsing: " + e.getMessage());
            
            // Attempt to extract valid parts as much as possible
            try {
                int lastValidIndex = findLastValidJsonIndex(json);
                if (lastValidIndex > 0) {
                    String truncatedJson = json.substring(0, lastValidIndex);
                    return JsonParser.parseString(truncatedJson).getAsJsonObject();
                }
            } catch (Exception innerE) {
                System.err.println("Recovery attempt also failed: " + innerE.getMessage());
            }
            
            return null;
        }
    }
    
    // Robust Gson configuration
    public static Gson createRobustGson() {
        return new GsonBuilder()
            .setLenient() // Allow non-strict JSON
            .serializeNulls() // Serialize null values
            .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) // Naming convention
            .registerTypeAdapter(String.class, new StringDeserializer()) // Custom deserializer
            .registerTypeAdapter(Integer.class, new SafeIntegerDeserializer()) // Safe integer deserializer
            .create();
    }
    
    // Simple valid JSON index search (requires more robust implementation in practice)
    private static int findLastValidJsonIndex(String json) {
        int braceCount = 0;
        for (int i = 0; i < json.length(); i++) {
            char c = json.charAt(i);
            if (c == '{') braceCount++;
            else if (c == '}') braceCount--;
            
            if (braceCount == 0 && i > 0) {
                return i + 1;
            }
        }
        return -1;
    }
    
    // Custom string deserializer
    static class StringDeserializer implements JsonDeserializer<String> {
        @Override
        public String deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) 
                throws JsonParseException {
            if (json.isJsonNull()) {
                return null;
            }
            if (json.isJsonPrimitive()) {
                return json.getAsString();
            }
            // Convert non-strings to string
            return json.toString();
        }
    }
    
    // Safe integer deserializer
    static class SafeIntegerDeserializer implements JsonDeserializer<Integer> {
        @Override
        public Integer deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) 
                throws JsonParseException {
            if (json.isJsonNull()) {
                return null;
            }
            
            try {
                if (json.isJsonPrimitive()) {
                    JsonPrimitive primitive = json.getAsJsonPrimitive();
                    if (primitive.isNumber()) {
                        return primitive.getAsInt();
                    }
                    if (primitive.isString()) {
                        return Integer.parseInt(primitive.getAsString());
                    }
                }
                return 0; // Default value
            } catch (NumberFormatException e) {
                System.err.println("Integer conversion error, using default value 0: " + json);
                return 0;
            }
        }
    }
    
    // Deserialization with performance metrics
    public static <T> T deserializeWithMetrics(String json, Class<T> clazz) {
        long startTime = System.nanoTime();
        long memoryBefore = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
        
        try {
            Gson gson = new Gson();
            T result = gson.fromJson(json, clazz);
            
            long endTime = System.nanoTime();
            long memoryAfter = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
            
            double durationMs = (endTime - startTime) / 1_000_000.0;
            long memoryUsed = memoryAfter - memoryBefore;
            
            System.out.println("Deserialization time: " + String.format("%.2f", durationMs) + "ms");
            System.out.println("Memory usage: " + memoryUsed + " bytes");
            
            return result;
        } catch (Exception e) {
            long endTime = System.nanoTime();
            double durationMs = (endTime - startTime) / 1_000_000.0;
            System.err.println("Error occurred (elapsed time: " + String.format("%.2f", durationMs) + "ms): " + e.getMessage());
            return null;
        }
    }
    
    public static void main(String[] args) {
        System.out.println("=== Gson Error Handling Example ===");
        comprehensiveErrorHandling();
        
        // Performance metrics deserialization test
        System.out.println("\n=== Performance Measurement Example ===");
        String testJson = new Gson().toJson(new User(1, "Metrics Test", "[email protected]", true));
        User metricsUser = deserializeWithMetrics(testJson, User.class);
        if (metricsUser != null) {
            System.out.println("Measurement result: " + metricsUser.getName());
        }
    }
}

Instance Creation and Custom Adapters

import com.google.gson.*;
import java.lang.reflect.Type;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

// Custom InstanceCreator
class UserInstanceCreator implements InstanceCreator<User> {
    @Override
    public User createInstance(Type type) {
        // Create User instance with default values
        return new User(0, "Default User", "[email protected]", false);
    }
}

// Complex object adapter
class ConfigurationAdapter implements JsonSerializer<Configuration>, JsonDeserializer<Configuration> {
    
    @Override
    public JsonElement serialize(Configuration src, Type typeOfSrc, JsonSerializationContext context) {
        JsonObject jsonObject = new JsonObject();
        jsonObject.addProperty("version", src.getVersion());
        jsonObject.addProperty("environment", src.getEnvironment());
        jsonObject.add("features", context.serialize(src.getFeatures()));
        jsonObject.addProperty("timestamp", src.getCreatedAt().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
        
        // Exclude sensitive information
        if (!src.getEnvironment().equals("production")) {
            jsonObject.addProperty("debug", src.isDebugMode());
        }
        
        return jsonObject;
    }
    
    @Override
    public Configuration deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) 
            throws JsonParseException {
        JsonObject jsonObject = json.getAsJsonObject();
        
        Configuration config = new Configuration();
        config.setVersion(jsonObject.get("version").getAsString());
        config.setEnvironment(jsonObject.get("environment").getAsString());
        
        // Deserialize features
        Type listType = new TypeToken<List<String>>(){}.getType();
        List<String> features = context.deserialize(jsonObject.get("features"), listType);
        config.setFeatures(features);
        
        // Convert timestamp to LocalDateTime
        String timestamp = jsonObject.get("timestamp").getAsString();
        config.setCreatedAt(LocalDateTime.parse(timestamp, DateTimeFormatter.ISO_LOCAL_DATE_TIME));
        
        // Set debug field only if it exists
        if (jsonObject.has("debug")) {
            config.setDebugMode(jsonObject.get("debug").getAsBoolean());
        }
        
        return config;
    }
}

// Configuration class
class Configuration {
    private String version;
    private String environment;
    private List<String> features;
    private boolean debugMode;
    private LocalDateTime createdAt;
    
    public Configuration() {
        this.createdAt = LocalDateTime.now();
        this.features = new ArrayList<>();
    }
    
    // getter/setter
    public String getVersion() { return version; }
    public void setVersion(String version) { this.version = version; }
    public String getEnvironment() { return environment; }
    public void setEnvironment(String environment) { this.environment = environment; }
    public List<String> getFeatures() { return features; }
    public void setFeatures(List<String> features) { this.features = features; }
    public boolean isDebugMode() { return debugMode; }
    public void setDebugMode(boolean debugMode) { this.debugMode = debugMode; }
    public LocalDateTime getCreatedAt() { return createdAt; }
    public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
}

public class AdvancedAdapterExample {
    
    public static void demonstrateCustomAdapters() {
        // Create Gson with registered custom adapters
        Gson gson = new GsonBuilder()
            .registerTypeAdapter(User.class, new UserInstanceCreator())
            .registerTypeAdapter(Configuration.class, new ConfigurationAdapter())
            .setPrettyPrinting()
            .create();
        
        // Create Configuration object
        Configuration config = new Configuration();
        config.setVersion("2.1.0");
        config.setEnvironment("development");
        config.setFeatures(Arrays.asList("auth", "logging", "metrics", "cache"));
        config.setDebugMode(true);
        
        // Serialization
        String configJson = gson.toJson(config);
        System.out.println("Configuration JSON:\n" + configJson);
        
        // Deserialization
        Configuration deserializedConfig = gson.fromJson(configJson, Configuration.class);
        System.out.println("Deserialized result:");
        System.out.println("Version: " + deserializedConfig.getVersion());
        System.out.println("Environment: " + deserializedConfig.getEnvironment());
        System.out.println("Feature count: " + deserializedConfig.getFeatures().size());
        System.out.println("Debug mode: " + deserializedConfig.isDebugMode());
        
        // Production environment behavior (debug field excluded)
        config.setEnvironment("production");
        String prodJson = gson.toJson(config);
        System.out.println("\nProduction environment JSON:\n" + prodJson);
        
        // InstanceCreator behavior verification
        String emptyUserJson = "{}"; // UserInstanceCreator works even with empty JSON
        User defaultUser = gson.fromJson(emptyUserJson, User.class);
        System.out.println("\nInstanceCreator result: " + defaultUser.getName());
    }
    
    public static void main(String[] args) {
        System.out.println("=== Custom Adapter Example ===");
        demonstrateCustomAdapters();
    }
}