Gson @Expose vs @SerializedName
1. Introduction
Gson @Expose and @SerializedName annotations are provided by the Gson library to control serialization and deserialization behavior when converting an object to JSON and vice versa. They serve different purposes but can be used together. I hope you enjoy our Gson @Exposed vs @SerializedName article.
Here are the @Expose and @SerializedName definitions from the com.google.gson.annotations package.
@Expose
/*An annotation that indicates this member should be exposed for JSON serialization or deserialization. This annotation has no effect unless you build Gson with a GsonBuilder and invoke GsonBuilder.excludeFieldsWithoutExposeAnnotation() method.*/ @Documented @Retention(RUNTIME) @Target(FIELD) public @interface Expose
@SerializedName
/*An annotation that indicates this member should be serialized to JSON with the provided name value as its field name.
This annotation will override any FieldNamingPolicy setting.*/
@Documented
@Retention(RUNTIME)
@Target({FIELD,METHOD})
public @interface SerializedName
- Line 5: it defines both
FIELDandMETHOD, but Out-of-box Gson ignores method-level annotations. it uses field-based reflection today.
2. Setup
In this step, I will create a Gradle project along with Lombok and Gson libraries.
build.gradle
plugins {
id 'java'
id 'org.springframework.boot' version '3.5.0'
id 'io.spring.dependency-management' version '1.1.7'
}
group = 'org.jcg.zheng'
version = '0.0.1-SNAPSHOT'
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
// https://mvnrepository.com/artifact/com.google.code.gson/gson
implementation("com.google.code.gson:gson:2.13.1")
}
tasks.named('test') {
useJUnitPlatform()
}- Line 34: include the Gson library.
3. POJO With Expose and Serializedname Annotations
In this step, I will create a SomePojo.java that annotates with both @Expose and @SerializedName annotations.
SomePojo.java
package org.jcg.zheng.gsondemo.data;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
import lombok.Builder;
import lombok.Data;
@Data
@Builder
public class SomePojo {
private String defaultName;
@SerializedName("exposedChangedName4")
@Expose(serialize = true, deserialize = true)
private String exposedChangedName;
@Expose(serialize = true, deserialize = true)
private String exposedName;
@SerializedName(value = "jsonName")
private String javaName;
@SerializedName(value = "name1", alternate = { "name2", "name3" })
String name;
@Expose(serialize = false, deserialize = false)
private String notExposedName;
}
- Line 15, 16: the
exposedChangedNamefield is annotated with both@Exposeand@SerializedName. So serializes/deserializes theexposedChangedNamefield with the JSON key “exposedChangedName4” and ensures that theserialize = true, deserialize = truesetting ensures that it is included if usingexcludeFieldsWithoutExposeAnnotation(). - Line 19: the
exposedNamefield is included when usingexcludeFieldsWithoutExposeAnnotation()as it’s annotatedwith @Expose(serialize = true, deserialize = true. - Line 22: map
javaNametojsonNamevia@SerializedName(value = "jsonName"). - Line 25: map
nameto “name1“, “name2“, or “name3” via@SerializedName‘salternateattribute. When deserializing from JSON to POJO, any fields of “name1“, “name2“, or “name3” can be mapped to thename. The last one in the JSON will be used if many similar fields exist. - Line 28: the
notExposedNameis not included as bothserializeanddeserializeattributes are set tofalsewhen usingexcludeFieldsWithoutExposeAnnotation().
4. GsonConverter
In this step, I will create a GsonConverter.java that converts a SomePojo object to JSON String and vice versa.
GsonConverter.java
package org.jcg.zheng.gsondemo.converter;
import org.jcg.zheng.gsondemo.data.SomePojo;
import com.google.gson.Gson;
public class GsonConverter {
public String toJson(final Gson gson, final SomePojo obj) {
return gson.toJson(obj);
}
public SomePojo fromJson(final Gson gson, final String jsonString) {
return gson.fromJson(jsonString, SomePojo.class);
}
}
5. Test for Expose and Serializedname Annotations
In this step, I will create a GsonConverterTest.java class to demonstrate how @Expose and @SerializedName annotations affected the JSON serialization and deserialization.
GsonConverterTest.java
package org.jcg.zheng.gsondemo.converter;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import org.jcg.zheng.gsondemo.data.SomePojo;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import com.google.gson.FieldNamingPolicy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
class GsonConverterTest {
private Gson gson;
private Gson gsonWithExpose;
private GsonConverter testClass = new GsonConverter();
private SomePojo testObj;
@BeforeEach
void setup() {
testObj = SomePojo.builder().defaultName("Mary").javaName("zheng").exposedName("public")
.notExposedName("private").name("MaryZheng").name("someName").exposedChangedName("Sammo").build();
gson = new Gson();
gsonWithExpose = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
}
@Test
void test_from_to_Json_via_gson() {
String jsonStr = testClass.toJson(gson, testObj);
System.out.println("test_from_to_Json_via_gson " + jsonStr);
assertEquals(
"{\"defaultName\":\"Mary\",\"exposedChangedName4\":\"Sammo\",\"exposedName\":\"public\",\"jsonName\":\"zheng\",\"name1\":\"someName\",\"notExposedName\":\"private\"}",
jsonStr);
SomePojo convertedObj = testClass.fromJson(gson, jsonStr);
assertEquals("Mary", convertedObj.getDefaultName());
assertEquals("zheng", convertedObj.getJavaName());
assertEquals("public", convertedObj.getExposedName());
assertEquals("private", convertedObj.getNotExposedName());
assertEquals("someName", convertedObj.getName());
assertEquals("Sammo", convertedObj.getExposedChangedName());
}
@Test
void test_from_to_Json_via_gsonWithExpose() {
String jsonStr = testClass.toJson(gsonWithExpose, testObj);
System.out.println("test_from_to_Json_via_gsonWithExpose" + jsonStr);
assertEquals("{\"exposedChangedName4\":\"Sammo\",\"exposedName\":\"public\"}", jsonStr);
SomePojo convertedObj = testClass.fromJson(gsonWithExpose, jsonStr);
assertNull(convertedObj.getDefaultName());
assertNull(convertedObj.getJavaName());
assertNull(convertedObj.getNotExposedName());
assertNull(convertedObj.getName());
assertEquals("public", convertedObj.getExposedName());
assertEquals("Sammo", convertedObj.getExposedChangedName());
}
@Test
void test_gson_FieldNamingPolicy_case() {
Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE).create();
String jsonStr = testClass.toJson(gson, testObj);
System.out.println("test_gson_FieldNamingPolicy_case" + jsonStr);
assertEquals(
"{\"DefaultName\":\"Mary\",\"exposedChangedName4\":\"Sammo\",\"ExposedName\":\"public\",\"jsonName\":\"zheng\",\"name1\":\"someName\",\"NotExposedName\":\"private\"}",
jsonStr);
}
@Test
void test_gson_FieldNamingPolicy_dash() {
Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_DASHES).create();
String jsonStr = testClass.toJson(gson, testObj);
System.out.println("test_gson_FieldNamingPolicy_dash" + jsonStr);
assertEquals(
"{\"default-name\":\"Mary\",\"exposedChangedName4\":\"Sammo\",\"exposed-name\":\"public\",\"jsonName\":\"zheng\",\"name1\":\"someName\",\"not-exposed-name\":\"private\"}",
jsonStr);
}
@Test
void test_gson_FieldNamingPolicy_dot() {
Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_DOTS).create();
String jsonStr = testClass.toJson(gson, testObj);
System.out.println("test_gson_FieldNamingPolicy_dot" + jsonStr);
assertEquals(
"{\"default.name\":\"Mary\",\"exposedChangedName4\":\"Sammo\",\"exposed.name\":\"public\",\"jsonName\":\"zheng\",\"name1\":\"someName\",\"not.exposed.name\":\"private\"}",
jsonStr);
}
@Test
void test_gson_FieldNamingPolicy_space() {
Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE_WITH_SPACES).create();
String jsonStr = testClass.toJson(gson, testObj);
System.out.println("test_gson_FieldNamingPolicy_space" + jsonStr);
assertEquals(
"{\"Default Name\":\"Mary\",\"exposedChangedName4\":\"Sammo\",\"Exposed Name\":\"public\",\"jsonName\":\"zheng\",\"name1\":\"someName\",\"Not Exposed Name\":\"private\"}",
jsonStr);
}
@Test
void test_gson_FieldNamingPolicy_underscore() {
Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
String jsonStr = testClass.toJson(gson, testObj);
System.out.println("test_gson_FieldNamingPolicy_underscore" + jsonStr);
assertEquals(
"{\"default_name\":\"Mary\",\"exposedChangedName4\":\"Sammo\",\"exposed_name\":\"public\",\"jsonName\":\"zheng\",\"name1\":\"someName\",\"not_exposed_name\":\"private\"}",
jsonStr);
}
@Test
void test_serializedname_alternate_gson() {
String testJson = "{'name1':'v1'}";
SomePojo target = testClass.fromJson(gson, testJson);
assertEquals("v1", target.getName());
testJson = "{'name2':'v2'}";
target = testClass.fromJson(gson, testJson);
assertEquals("v2", target.getName());
testJson = "{'name3':'v3'}";
target = testClass.fromJson(gson, testJson);
assertEquals("v3", target.getName());
//the last one in JSON will be used
testJson = "{'name1':'v1', 'name2':'v2'}";
target = testClass.fromJson(gson, testJson);
assertEquals("v2", target.getName());
testJson = "{'name2':'v2', 'name1':'v1'}";
target = testClass.fromJson(gson, testJson);
assertEquals("v1", target.getName());
testJson = "{'name1':'v1', 'name3':'v3'}";
target = testClass.fromJson(gson, testJson);
assertEquals("v3", target.getName());
testJson = "{'name1':'v1', 'name2':'v2', 'name3':'v3'}";
target = testClass.fromJson(gson, testJson);
assertEquals("v3", target.getName());
testJson = "{'name2':'v2', 'name3':'v3'}";
target = testClass.fromJson(gson, testJson);
assertEquals("v3", target.getName());
testJson = "{'name3':'v3', 'name2':'v2'}";
target = testClass.fromJson(gson, testJson);
assertEquals("v2", target.getName());
}
@Test
void test_serializedname_alternate_gsonWithExpose() {
String testJson = "{\"Default Name\":\"Mary\",\"exposedChangedName4\":\"Sammo\",\"Exposed Name\":\"public\",\"jsonName\":\"zheng\",\"name1\":\"someName\",\"Not Exposed Name\":\"private\"}";
SomePojo target = testClass.fromJson(gsonWithExpose, testJson);
assertNull(target.getName());
assertEquals("Sammo", target.getExposedChangedName());
testJson = "{'name2':'v2'}";
target = testClass.fromJson(gsonWithExpose, testJson);
assertNull(target.getName());
testJson = "{'name3':'v3'}";
target = testClass.fromJson(gsonWithExpose, testJson);
assertNull(target.getName());
}
}
- Line 23: the
testObjobject sets all the fields. You will see the serialized JSONs are different between the defaultgsonandgsonWithExposeobjects. - Line 26: create a
gsonobject with the default setting. It includes all the fields during the serialization and deserialization. - Line 27: create a customized
gsonWithExposeobject viaexcludeFieldsWithoutExposeAnnotation.It only processes these fields annotated with@Exposeannotation. - Line 31:
test_from_to_Json_via_gsonverifies the default gson’s serialization and deserialization work as expected. All fields’ data are mapped as lines 40-45 show. - Line 49:
test_from_to_Json_via_gsonWithExposeverified the customizedgsonWithExposewithexcludeFieldsWithoutExposeAnnotationonly serializes and deserializes these fields marked with@Exposeannotation as lines 55-62 show. - Line 65, 76, 87, 98, 109: verified
@SerializedNameoverwrites theFieldNamingPolicysetting. You can confirm by viewing the JSON string in the test results. - Line 121:
test_serializedname_alternate_gsonverifies thatname1,name2, andname3map to thenamefield because of thealternateattribute setting when using thegsonobject. It also confirms that the last one in the JSON will be mapped tonameifname1,name2, andname3co-exist in JSON. - Line 161:
test_serializedname_alternate_gsonWithExposeis used to compare withtest_serializedname_alternate_gson. It is not mapped withgsonWithExposebecause thenamefield is not annotated with@Expose.
Run the Junit tests and capture the results.
Junit Test Output
test_gson_FieldNamingPolicy_case{"DefaultName":"Mary","exposedChangedName4":"Sammo","ExposedName":"public","jsonName":"zheng","name1":"someName","NotExposedName":"private"}
test_gson_FieldNamingPolicy_dash{"default-name":"Mary","exposedChangedName4":"Sammo","exposed-name":"public","jsonName":"zheng","name1":"someName","not-exposed-name":"private"}
test_gson_FieldNamingPolicy_dot{"default.name":"Mary","exposedChangedName4":"Sammo","exposed.name":"public","jsonName":"zheng","name1":"someName","not.exposed.name":"private"}
test_gson_FieldNamingPolicy_underscore{"default_name":"Mary","exposedChangedName4":"Sammo","exposed_name":"public","jsonName":"zheng","name1":"someName","not_exposed_name":"private"}
test_from_to_Json_via_gson {"defaultName":"Mary","exposedChangedName4":"Sammo","exposedName":"public","jsonName":"zheng","name1":"someName","notExposedName":"private"}
test_gson_FieldNamingPolicy_space{"Default Name":"Mary","exposedChangedName4":"Sammo","Exposed Name":"public","jsonName":"zheng","name1":"someName","Not Exposed Name":"private"}
test_from_to_Json_via_gsonWithExpose{"exposedChangedName4":"Sammo","exposedName":"public"}
- the fields:
name1,exposedChangedName4, andjsonNameremain unchanged whenFieldNamingPolicychanged because they are marked with@SerializedName.
6. Conclusion
In this example, I explained both @Expose and @SerializedName annotations and demonstrated with Junit tests. The @Expose annotation’s purpose is to control whether a field should be included in serialization or deserialization. By default, Gson includes all fields, but if configured with the @Expose annotation, then only fields with @Expose annotation will be processed. The @SerializedName annotation’s purpose is to specify the name to be used. Table 1 outlines the summary of both annotations.
| Annotation | Purpose | Attributes | Need a Special Config? | Usage Example |
@Expose | include/exclude fields in JSON processing | serialize and deserialize | Yes, with excludeFieldsWithoutExposeAnnotation() | skipping sensitive data |
@SerializedName | rename field for Json key mapping and overwrite the FieldNamingPolicy | value and alternate | No | mapping “userId" to “user_id“ |
7. Download
This was an example of a Gradle project which included Gson expose and serializedname annotations examples.
You can download the full source code of this example here: Expose and SerializedName Annotations in Gson


