Search code examples
javajsondeserializationgsonfinal

Gson deserialization: set final field


i deserialize a widget-hierarchy using gson, but have problems deserializing final fields.

Example:

public final class Screen{

    @Expose
    private final List<WidgetDefinition> children       = null;
    @Expose
    private final String name           = null;
}

public final class AWidget implements WidgetDefinition {
    @Expose
    private final String name           = null;
}

i'm deserializing a Screen using a custom deserializer for WidgetDefinition, shown below. 'name' in Screen is set correctly, 'name' in AWidget stays null.

final class Deserializer implements JsonDeserializer<WidgetDefinition> {

    public WidgetDefinition deserialize(final JsonElement json, final Type type,
                                        final JsonDeserializationContext context) {

        JsonObject jsonObject = json.getAsJsonObject();

        String typeName = jsonObject.get("type").getAsString();
        if (typeName.equals("awidget")) {
            return context.deserialize(json, AWidget.class);
        } else {
            return null;
        }
    }
}

Edit: I wonder if it has to do something with this:

Gson 1.7 won’t serialize subclass fields in collection elements. 2.0 adds this extra information.

(https://sites.google.com/site/gson/gson-roadmap)


Solution

  • Gson uses reflection to set final fields (via .setAccessible(true)), so the problem you describe (and other related) probably comes from the way Java handles finals...

    JLS 17.5.3 Subsequent Modification of final Fields

    In some cases, such as deserialization, the system will need to change the final fields of an object after construction. final fields can be changed via reflection and other implementation-dependent means. The only pattern in which this has reasonable semantics is one in which an object is constructed and then the final fields of the object are updated. The object should not be made visible to other threads, nor should the final fields be read, until all updates to the final fields of the object are complete. Freezes of a final field occur both at the end of the constructor in which the final field is set, and immediately after each modification of a final field via reflection or other special mechanism.

    Even then, there are a number of complications. If a final field is initialized to a compile-time constant expression (§15.28) in the field declaration, changes to the final field may not be observed, since uses of that final field are replaced at compile time with the value of the constant expression.

    Another problem is that the specification allows aggressive optimization of final fields. Within a thread, it is permissible to reorder reads of a final field with those modifications of a final field that do not take place in the constructor.