Gson: How to Exclude Specific Fields from Serialization Without Annotations

Java Gson Exclude fields during serialization

In order to get this result, you need to annotate all the fields with the @Expose:

public class ConfigInstance {

@Expose
public String database_address;
@Expose
public int database_port;
@Expose
public String database_user;

@Expose(serialize = false)
private String database_pass;
@Expose
public String database_pass_hash;

And configure Gson to only expose fields that are annotated and ignore the rest as shown in the following:

Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().setPrettyPrinting().create();

Then, you'll get:

{
"database_address": "127.0.0.1",
"database_port": 1521,
"database_user": "test",
"database_pass_hash": "B9FE2C011B59F0D0D383D70073E48A19"
}

Also, when deserialising the string you'll still have the password attribute as well.


Still, you have the possibility to configure a Gson Serializer to accomplish this.

How to exclude fields during serialization of gson

I believe it has not much with Hibernate, but more with GSON. You should define serialization/deserialization rule.

Easiest way is to use @Expose annotation, to include/exclude given property from serialization/deserialization:

@Expose(serialize = false, deserialize = false)

Another way is to define custom adapters for givem class. WIth Adapter you can completely override the way GSON serialize or deserialize object.

For example:

public class YourAdapter implements JsonDeserializer<Order>, JsonSerializer<Order> {
@Override
public Orderde serialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
// your logic
}

@Override
public JsonElement serialize(Ordersrc, Type typeOfSrc, JsonSerializationContext context) {
// your logic
}
}

Finally initialize instance of your Gson parser:

Gson gson = new GsonBuilder()
.registerTypeAdapter(Order.class, new YourAdapter())
.build();

If you want to avoid writting whole serialization, you could exclude your field by using @Expose, and then write adapter with pure instance of GSON in it, serialize/deserialize using that pure one and manually add that one field.

Another way of dealing with serialization/deserialization problem is to use ExclusionStrategy described here: https://google-gson.googlecode.com/svn/trunk/gson/docs/javadocs/com/google/gson/ExclusionStrategy.html

Gson serialization: exclude SOME fields if null but not all of them


Using ExclusionStrategy, I can define the field, but I cannot seem to find a way to get the value.

Yes, it does not provide a way of determining the current field value. This is because of how Gson ReflectiveTypeAdapterFactory works internally (the BoundField.serialized is final and only resolved once):

@Override public boolean writeField(Object value) throws IOException, IllegalAccessException {
if (!serialized) return false;
Object fieldValue = field.get(value);
return fieldValue != value; // avoid recursion for example for Throwable.cause
}
for (BoundField boundField : boundFields.values()) {
if (boundField.writeField(value)) {
out.name(boundField.name);
boundField.write(out, value);
}
}

This behavior cannot be changed, but I believe it's a good design choice to segregate application objects and their serialized representations (see the Data Transfer Object pattern) in order not to mix concepts and make applicaiton components loosely coupled (migrating from Gson someday would take only modifications for the respective DTO classes only).

If you're fine with having DTOs introduced to your application, then you could create separate DTO classes for both scenarios: preserving phone and discarding fax depending on the fax field value.

class PersonDto {
@Expose String name;
@Expose String phone;
PersonDto(final Person person) {
name = person.name;
phone = person.phone;
}
}
class PersonDtoWithFax extends PersonDto {
@Expose String fax;
PersonDtoWithFax(final Person person) {
super(person);
fax = person.fax;
}
}

In this case the serialization is straight-forward:

final Gson gson = new GsonBuilder()
.serializeNulls()
.create();
final Person person = new Person();
person.name = "John";
final PersonDto personDto = person.fax == null
? new PersonDto(person)
: new PersonDtoWithFax(person);
System.out.println(gson.toJson(personDto));

If you don't want to introduce the segregated DTO concept per se, you probably might want to implement a custom serializer that is somewhat more complicated in implementation and probably somewhat error-prone due to property names hardcoding (but you can have good tests, of course, or extract the names from java.lang.reflect.Field instances).

final class SpecialJsonSerializer<T>
implements JsonSerializer<T> {

private final Gson gson; // Unfortunately, Gson does not provide much from JsonSerialiationContext, so we have to get it ourselves
private final Iterable<String> excludeIfNull;

private SpecialJsonSerializer(final Gson gson, final Iterable<String> excludeIfNull) {
this.gson = gson;
this.excludeIfNull = excludeIfNull;
}

static <T> JsonSerializer<T> getSpecialJsonSerializer(final Gson gson, final Iterable<String> excludeIfNull) {
return new SpecialJsonSerializer<>(gson, excludeIfNull);
}

@Override
public JsonElement serialize(final T object, final Type type, final JsonSerializationContext context) {
// context.serialize(person, type) cannot work due to infinite recursive serialization
// therefore the backing Gson instance is used
final JsonObject jsonObject = gson.toJsonTree(object, type).getAsJsonObject();
for ( final String propertyName : excludeIfNull ) {
final JsonElement property = jsonObject.get(propertyName);
if ( property != null && property.isJsonNull() ) {
jsonObject.remove(propertyName);
}
}
return jsonObject;
}

}

I'm not really sure, but I think that creating JSON trees for serialization purposes rather than using DTOs may be slightly more expensive from the memory consumption point of view (at least because of more complicated JsonElement structure).

// Both Gson instances must have serializeNulls()
final Gson gson = new GsonBuilder()
.serializeNulls()
.create();
final Gson gsonWrapper = new GsonBuilder()
.serializeNulls()
.registerTypeAdapter(Person.class, getSpecialJsonSerializer(gson, singletonList("fax")))
.create();
final Person person = new Person();
person.name = "John";
System.out.println(gsonWrapper.toJson(person));

Both solutions output:

{"name":"John","phone":null}

Gson ignore field on serialization under condition

You might be looking for Exclusion Strategies - Look at the following link to the GSON documentation for further details: https://howtodoinjava.com/gson/gson-exclude-or-ignore-fields/ :)

Excluding certain fields from Serialization based on value in GSON

The way to achieve this is by creating custom serializer for the class in question. After allowing Gson to create a JSON object in default fashion, remove the property that you want to exclude based on its value.

public class SerializerForMyClass implements JsonSerializer<MyClass> {  

@Override
public JsonElement serialize(MyClass obj, Type type, JsonSerializationContext jsc) {
Gson gson = new Gson();
JsonObject jObj = (JsonObject)gson.toJsonTree(obj);
if(obj.getMyProperty()==0){
jObj.remove("myProperty");
}
return jObj;
}
}

And registering the new serializer in the Gson object that you use for serialization in the application for this class.

GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(MyClass.class, new SerializerForMyClass());
Gson gson=gsonBuilder.create();
gson.toJson(myObjectOfTypeMyClass);


Related Topics



Leave a reply



Submit