Search code examples
javajsonserializationgsontransient

Deserialize JSON into transient field


I have a class with these fields:

private transient List<Peer> peers;
private final String name;
private final int points;
private final int size;

Using Gson I want to deserialize this JSON String request:

{
    "name": "game1",
    "points": "11",
    "size": "10",
    "peers": [
        {
            "address": "localhost",
            "port": 1234,
            "fullAddress": "localhost:1234"
        }
    ]
}

My problem is that the Peer object does not get deserialized into the peers List unless I don't declare the field as transient.

Is there a way, with Gson, to have some field transient only during serialization but not during deserialization?


Solution

  • You have two options.

    excludeFieldsWithoutExposeAnnotation()

    Gson provides @Expose that serves the exact purpose. The only caveat here is that you have to annotate every field:

    private static final Gson gson = new GsonBuilder()
            .excludeFieldsWithoutExposeAnnotation()
            .create();
    
    @Expose(serialize = false) final List<Peer> peers;
    @Expose final String name;
    @Expose final int points;
    @Expose final int size;
    

    addSerializationExclusionStrategy(...)

    Say, you can easily introduce something like this:

    @Target(FIELD)
    @Retention(RUNTIME)
    @interface ReadOnly {
    }
    

    Now, once this one is declared, you can register a strategy to the Gson instance:

    private static final Gson gson = new GsonBuilder()
            .addSerializationExclusionStrategy(new ExclusionStrategy() {
                @Override
                public boolean shouldSkipField(final FieldAttributes f) {
                    return f.getAnnotation(ReadOnly.class) != null;
                }
    
                @Override
                public boolean shouldSkipClass(final Class<?> clazz) {
                    return false;
                }
            })
            .create();
    
    @ReadOnly final List<Peer> peers;
    final String name;
    final int points;
    final int size;
    

    You can easily use @Expose for the option #2 by just handling it with something like f.getAnnotation(Expose.class) != null && !f.getAnnotation(Expose.class).serialize() in the strategy, but I find @ReadOnly somewhat more convenient.

    For both options, the following code

    public static void main(final String... args)
            throws IOException {
        try ( final JsonReader jsonReader = getPackageResourceJsonReader(Q43893428.class, "foo.json") ) {
            final Foo foo = gson.fromJson(jsonReader, Foo.class);
            for ( final Peer peer : foo.peers ) {
                System.out.println(peer.fullAddress);
            }
            System.out.println(gson.toJson(foo));
        }
    }
    

    produces the following result:

    localhost:1234
    {"name":"game1","points":11,"size":10}