# GSON

Gson mainly build for Java, other jvm languages may not work properly. It&#x20;

* Provide simple `toJson()` and `fromJson()` methods to convert Java objects to JSON and vice-versa
* Allow pre-existing unmodifiable objects to be converted to and from JSON
* Extensive support of Java Generics and Allow custom representations for objects

### Dependency

```xml
<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>${version}</version>
</dependency>

```

### Some points to consider&#x20;

* We can use private fields, they will be serialized.
* All fields in the current class (and from all super classes) are included by default.
* If a field is marked transient, (by default) it is ignored and not included in the JSON serialization or deserialization.
* This implementation handles nulls correctly.
  * While serializing, a null field is omitted from the output.
  * While deserializing, a missing entry in JSON results in setting the corresponding field in the object to its default value: null for object types, zero for numeric types, and false for booleans.
* If a field is *synthetic*, it is ignored and not included in JSON serialization or deserialization.
* Fields corresponding to the outer classes in inner classes are ignored and not included in serialization or deserialization.
* Anonymous and local classes are excluded. They will be serialized as JSON `null` and when deserialized their JSON value is ignored and `null` is returned. Convert the classes to `static` nested classes to enable serialization and deserialization for them.
* We cannot serialize objects with circular references, since that will result in infinite recursion.

### Serialization and De-serialization&#x20;

First we need to create Gson object by using the class **Gson,** then we can use methods like **fromJson & toJson** to do objects and json conversion.

### TypeToken

TypeToken is a class in Gson that helps capture and preserve generic type information.&#x20;

TypeToken is essential when working with generic types in Gson, and understanding its proper usage helps in handling complex JSON structures effectively.&#x20;

TypeToken instances should be created carefully and reused when possible for better performance.

{% hint style="info" %}
[For more details go to TypeToken section](https://wisdom.gitbook.io/gyan/java/gson/type-token)
{% endhint %}

### Examples&#x20;

#### **Primitives**

```java
// Serialization
Gson gson = new Gson();
gson.toJson(1);            // ==> 1
gson.toJson("abcd");       // ==> "abcd"
gson.toJson(new Long(10)); // ==> 10
int[] values = { 1 };
gson.toJson(values);       // ==> [1]

// Deserialization
int i = gson.fromJson("1", int.class);

Integer intObj = gson.fromJson("1", Integer.class);

Long longObj = gson.fromJson("1", Long.class);

Boolean boolObj = gson.fromJson("false", Boolean.class);

String str = gson.fromJson("\"abc\"", String.class);

String[] strArray = gson.fromJson("[\"abc\"]", String[].class);
```

### Arrays Example&#x20;

```java
Gson gson = new Gson();
int[] ints = {1, 2, 3, 4, 5};
String[] strings = {"abc", "def", "ghi"};

// Serialization
gson.toJson(ints);     // ==> [1,2,3,4,5]

gson.toJson(strings);  // ==> ["abc", "def", "ghi"]

// Deserialization
int[] ints2 = gson.fromJson("[1,2,3,4,5]", int[].class);
// ==> ints2 will be same as ints
```

### Collections Example&#x20;

for the collections, we will be needing type tokens for conversion.

```java
Gson gson = new Gson();
Collection<Integer> ints = Arrays.asList(1,2,3,4,5);

// Serialization
String json = gson.toJson(ints);  // ==> [1,2,3,4,5]

// Deserialization
TypeToken<Collection<Integer>> collectionType = new TypeToken<Collection<Integer>>(){};

Collection<Integer> ints2 = gson.fromJson(json, collectionType);
// ==> ints2 is same as ints

String json = "[\"Alice\", \"Bob\", \"Charlie\"]";

// Use TypeToken to define the type
Type listType = new TypeToken<List<String>>() {}.getType();

// Deserialize JSON to List<String>
List<String> names = gson.fromJson(json, listType);
System.out.println("Names: " + names);

// Serialize List<String> back to JSON
String jsonString = gson.toJson(names, listType);


```

### Maps Examples

Gson by default serializes any `java.util.Map` implementation as a JSON object. Because JSON objects only support strings as member names, Gson converts the Map keys to strings by calling `toString()` on them, and using `"null"` for `null` keys:

```java
Gson gson = new Gson();

Map<String, String> stringMap = new LinkedHashMap<>();
stringMap.put("key", "value");
stringMap.put(null, "null-entry");

// Serialization
String json = gson.toJson(stringMap); // ==> {"key":"value","null":"null-entry"}

Map<Integer, Integer> intMap = new LinkedHashMap<>();
intMap.put(2, 4);
intMap.put(3, 6);

// Serialization
String json = gson.toJson(intMap); // ==> {"2":4,"3":6}
```

for deserialization a `TypeToken` has to be used to tell Gson what types the Map keys and values have:

```java
Gson gson = new Gson();
TypeToken<Map<String, String>> mapType = new TypeToken<Map<String, String>>(){};
String json = "{\"key\": \"value\"}";

// Deserialization
Map<String, String> stringMap = gson.fromJson(json, mapType);
// ==> stringMap is {key=value}
```

Another Example&#x20;

```java
String json = "[{\"Alice\": 25, \"Bob\": 30}, {\"Charlie\": 35, \"David\": 40}]";
Gson gson = new Gson();

// Use TypeToken to define the type
Type nestedType = new TypeToken<List<Map<String, Integer>>>() {}.getType();

// Deserialize JSON to List<Map<String, Integer>>
List<Map<String, Integer>> nestedList = gson.fromJson(json, nestedType);
System.out.println("Nested List: " + nestedList);

// Serialize List<Map<String, Integer>> back to JSON
String jsonString = gson.toJson(nestedList, nestedType);
System.out.println("JSON: " + jsonString);
```

If map keys are complex and toString method is not implemented, then this can lead to malformed encoded keys or can cause mismatch between serialization and deserialization of the keys.\
A workaround for this can be to use <mark style="color:purple;">`enableComplexMapKeySerialization`</mark>`()` to make sure the `TypeAdapter` registered for the Map key type is used for deserialization *and* serialization.

```java
class PersonName {
  String firstName;
  String lastName;

  PersonName(String firstName, String lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
  }

}

Gson gson = new GsonBuilder().enableComplexMapKeySerialization().create();
Map<PersonName, Integer> complexMap = new LinkedHashMap<>();
complexMap.put(new PersonName("John", "Doe"), 30);
complexMap.put(new PersonName("Jane", "Doe"), 35);

// Serialization; 
String json = gson.toJson(complexMap);
// ==> [[{"firstName":"John","lastName":"Doe"},30],[{"firstName":"Jane","lastName":"Doe"},35]]

Map<String, String> stringMap = new LinkedHashMap<>();
stringMap.put("key", "value");
// Serialization; 
String json = gson.toJson(stringMap); // ==> {"key":"value"}
```

### Raw Collection Example

```java
    Gson gson = new Gson();
    Collection collection = new ArrayList();
    collection.add("hello");
    collection.add(5);
    collection.add(new Event("GREETINGS", "guest"));
    
    String json = gson.toJson(collection);
    System.out.println("Using Gson.toJson() on a raw collection: " + json);
    
    JsonArray array = JsonParser.parseString(json).getAsJsonArray();
    String message = gson.fromJson(array.get(0), String.class);
    int number = gson.fromJson(array.get(1), int.class);
    Event event = gson.fromJson(array.get(2), Event.class);
    
    System.out.printf("Using Gson.fromJson() to get: %s, %d, %s", message, number, event);
```

### Objects Types-

```java
class Person {
    String name;
    int age;
    boolean isDeveloper;
}

Gson gson = new Gson();
// Serialization
String json = gson.toJson(person);

// --output 
{"name":"Alice","age":25,"isDeveloper":true}

String json = "{\"name\":\"Alice\",\"age\":25,\"isDeveloper\":true}";

// deSerialization
Person person = gson.fromJson(json, Person.class);
```

If Object is in with collections -

```java
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        String json = "[{\"name\": \"Alice\", \"age\": 25}, {\"name\": \"Bob\", \"age\": 30}]";
        Gson gson = new Gson();

        // Use TypeToken to define the type
        Type listType = new TypeToken<List<Person>>() {}.getType();

        // Deserialize JSON to List<Person>
        List<Person> people = gson.fromJson(json, listType);
        for (Person person : people) {
            System.out.println(person.name + " is " + person.age + " years old.");
        }

        // Serialize List<Person> back to JSON
        String jsonString = gson.toJson(people, listType);
        System.out.println("JSON: " + jsonString);
    }
}

```

### Nested Structure with Multiple Types

```java
public class Department {
    private String name;
    private Employee manager;
    private List<Employee> employees;
    private Map<String, Project> projects;
    private Set<String> locations;
    
    // Constructor, getters, setters
}

public class Project {
    private String name;
    private Date startDate;
    private List<Employee> team;
    private Map<String, Object> metadata;
    
    // Constructor, getters, setters
}

// Usage Example
public class ComplexStructureExample {
    public static void main(String[] args) {
        Gson gson = new GsonBuilder()
            .setPrettyPrinting()
            .setDateFormat("yyyy-MM-dd")
            .create();
        
        // Create complex structure
        Department dept = new Department();
        dept.setName("Engineering");
        dept.setManager(new Employee("John Manager", new Address("456 Boss St", "Boston", "USA", 12345)));
        dept.setEmployees(Arrays.asList(
            new Employee("Dev1", new Address("123 Dev St", "Boston", "USA", 12345)),
            new Employee("Dev2", new Address("124 Dev St", "Boston", "USA", 12345))
        ));
        
        Map<String, Project> projects = new HashMap<>();
        Project project = new Project();
        project.setName("Project X");
        project.setStartDate(new Date());
        project.setTeam(Arrays.asList(new Employee("Dev1"), new Employee("Dev2")));
        
        Map<String, Object> metadata = new HashMap<>();
        metadata.put("priority", 1);
        metadata.put("tags", Arrays.asList("urgent", "innovative"));
        project.setMetadata(metadata);
        
        projects.put("PRJ-1", project);
        dept.setProjects(projects);
        
        dept.setLocations(new HashSet<>(Arrays.asList("Boston", "New York", "San Francisco")));
        
        // Serialize
        String jsonDept = gson.toJson(dept);
        
        // Deserialize
        Department parsedDept = gson.fromJson(jsonDept, Department.class);
    }
}
```

### Generic Types&#x20;

When you call `toJson(obj)`, Gson calls `obj.getClass()` to get information on the fields to serialize. Similarly, you can typically pass `MyClass.class` object in the `fromJson(json, MyClass.class)` method. This works fine if the object is a non-generic type.

However, if the object is of a generic type, then the Generic type information is lost because of Java Type Erasure. To make it work properly we need to use TypeToken

```java
class Foo<T> {
  T value;
}
Gson gson = new Gson();
Foo<Bar> foo = new Foo<Bar>();

Type fooType = new TypeToken<Foo<Bar>>() {}.getType();
gson.toJson(foo, fooType);

gson.fromJson(json, fooType);
```

`fooType` actually defines an anonymous local inner class containing a method `getType()` that returns the fully parameterized type.

```java
{
  "status": "success",
  "data": [
    {"name": "Alice", "age": 25},
    {"name": "Bob", "age": 30}
  ]
}

class ApiResponse<T> {
    String status;
    T data;
}

class Person {
    String name;
    int age;
}

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

public class Main {
    public static void main(String[] args) {
        String json = "{\"status\": \"success\", \"data\": [{\"name\": \"Alice\", \"age\": 25}, {\"name\": \"Bob\", \"age\": 30}]}";
        Gson gson = new Gson();

        // Define the TypeToken for ApiResponse<List<Person>>
        Type responseType = new TypeToken<ApiResponse<List<Person>>>() {}.getType();

        // Deserialize JSON to ApiResponse<List<Person>>
        ApiResponse<List<Person>> response = gson.fromJson(json, responseType);
        System.out.println("Status: " + response.status);
        for (Person person : response.data) {
            System.out.println(person.name + " is " + person.age + " years old.");
        }

        // Serialize ApiResponse<List<Person>> back to JSON
        String jsonString = gson.toJson(response, responseType);
        System.out.println("JSON: " + jsonString);
    }
}

```

### Custom Serialization and Deserialization

Sometimes the default representation is not what you want. This is often the case when dealing with library classes (DateTime, etc.). Gson allows you to register your own custom serializers and deserializers. This is done by defining two parts:

* JSON Serializers: Need to define custom serialization for an object
* JSON Deserializers: Needed to define custom deserialization for a type
* Instance Creators: Not needed if no-args constructor is available or a deserializer is registered

```java
GsonBuilder gson = new GsonBuilder();
gson.registerTypeAdapter(MyType2.class, new MyTypeAdapter());
gson.registerTypeAdapter(MyType.class, new MySerializer());
gson.registerTypeAdapter(MyType.class, new MyDeserializer());
gson.registerTypeAdapter(MyType.class, new MyInstanceCreator());
```

Custom Type Adapters

```java
class DateAdapter implements JsonSerializer<Date>, JsonDeserializer<Date> {
    private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");

    @Override
    public JsonElement serialize(Date date, Type type, JsonSerializationContext context) {
        return new JsonPrimitive(dateFormat.format(date));
    }

    @Override
    public Date deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException {
        try {
            return dateFormat.parse(json.getAsString());
        } catch (ParseException e) {
            throw new JsonParseException(e);
        }
    }
}

// Register the adapter
Gson gson = new GsonBuilder()
.registerTypeAdapter(Date.class, new DateAdapter()).create();
```

{% hint style="info" %}
[Check custom Joda datetime Serializer and deserializer with Gson ](https://wisdom.gitbook.io/gyan/java/gson/joda-datetime-custom-serializer-and-deserializer)
{% endhint %}

### Instance Creator

Typically, Instance Creators are needed when you are dealing with a library class that does NOT define a no-argument constructor.

Basic instance creator-

```java
private class MoneyInstanceCreator implements InstanceCreator<Money> {
  public Money createInstance(Type type) {
    return new Money("1000000", CurrencyCode.USD);
  }
}
```

Instance Creator with parameterized type

```java
public class Id<T> {
  private final Class<T> classOfId;
  private final long value;
  public Id(Class<T> classOfId, long value) {
    this.classOfId = classOfId;
    this.value = value;
  }
}

class IdInstanceCreator implements InstanceCreator<Id<?>> {
  public Id<?> createInstance(Type type) {
    Type[] typeParameters = ((ParameterizedType)type).getActualTypeArguments();
    Type idType = typeParameters[0]; // Id has only one parameterized type T
    return new Id((Class)idType, 0L);
  }
}
```

### &#x20;Pretty Printing for JSON Output Format

```java
Gson gson = new GsonBuilder().setPrettyPrinting().create();
String jsonOutput = gson.toJson(someObject);
```

### Null Object Support

By default null object fields are ignored. To serialize them -

```java
Gson gson = new GsonBuilder().serializeNulls().create();
```

```java
public class Foo {
  private final String s;
  private final int i;

  public Foo() {
    this(null, 5);
  }

  public Foo(String s, int i) {
    this.s = s;
    this.i = i;
  }
}

Gson gson = new GsonBuilder().serializeNulls().create();
Foo foo = new Foo();
String json = gson.toJson(foo);
System.out.println(json);

json = gson.toJson(null);
System.out.println(json);


{"s":null,"i":5}
null
```

### Excluding Fields From Serialization and Deserialization

By default, if you mark a field as `transient`, it will be excluded. As well, if a field is marked as `static` then by default it will be excluded.

We can control this by using modifiers-&#x20;

```java
Gson gson = new GsonBuilder()
    .excludeFieldsWithModifiers(Modifier.STATIC, Modifier.TRANSIENT, Modifier.VOLATILE)
    .create();
```

**User Defined Exclusion Strategies**

The following example shows how to exclude fields marked with a specific `@Foo` annotation and excludes top-level types (or declared field type) of class `String`.

```java
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface Foo {
  // Field tag only annotation
}

public class SampleObjectForTest {
  @Foo private final int annotatedField;
  private final String stringField;
  private final long longField;

}

// custom exclusion strategy
public class MyExclusionStrategy implements ExclusionStrategy {
  private final Class<?> typeToSkip;

  private MyExclusionStrategy(Class<?> typeToSkip) {
    this.typeToSkip = typeToSkip;
  }

  public boolean shouldSkipClass(Class<?> clazz) {
    return (clazz == typeToSkip);
  }

  public boolean shouldSkipField(FieldAttributes f) {
    return f.getAnnotation(Foo.class) != null;
  }
}

public static void main(String[] args) {
  Gson gson = new GsonBuilder()
      .setExclusionStrategies(new MyExclusionStrategy(String.class))
      .serializeNulls()
      .create();
      
  SampleObjectForTest src = new SampleObjectForTest();
  String json = gson.toJson(src);
  System.out.println(json);
}
```

The output is:

```
{"longField":1234}
```

### JSON Field Naming Support

For defining custom name we can use <mark style="color:purple;">`@SerializedName`</mark> annotation and can configure <mark style="color:purple;">`FieldNamingPolicy`</mark> to serilization of fields&#x20;

```java
private class SomeObject {
  @SerializedName("custom_naming") private final String someField;
  private final String someOtherField;

  public SomeObject(String a, String b) {
    this.someField = a;
    this.someOtherField = b;
  }
}

SomeObject someObject = new SomeObject("first", "second");

Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE).create();

String jsonRepresentation = gson.toJson(someObject);
System.out.println(jsonRepresentation);

//-- output
{"custom_naming":"first","SomeOtherField":"second"}
```

### Best Practices -&#x20;

1. Use Pretty Printing for Debug
2. Handle Null Values Appropriately
3. Reuse Gson Instances

```java
@Configuration
public class GsonConfig {
    @Bean
    public Gson gson() {
        return new GsonBuilder()
            .setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ")
            .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
            .create();
    }
}
```

Inspired from - [gson user guide](https://github.com/google/gson/blob/main/UserGuide.md)
