Gson: Java 14/15 records can not set final field

Created on 8 Oct 2020  路  1Comment  路  Source: google/gson

Hi, as the following error describes, java records (preview feature) deserialization isn't working in gson, Jackson is adding support in its very soon release 2.12, is there going to be same support/fix in gson ?

java.lang.AssertionError: AssertionError (GSON 2.8.6): java.lang.IllegalAccessException: Can not set final java.lang.String field JsonGsonRecordTest$Emp.name to java.lang.String

Here's a sample test

  record Emp(String name) {}

  @Test
  void deserializeEngineer() {
    Gson j = new GsonBuilder().setPrettyPrinting().create();
    var empJson = """
            {
              "name": "bob"
            }""";
    var empObj = j.fromJson(empJson, Emp.class);
  }

Most helpful comment

The problem seems to exist only in Java 15 with preview enabled. Running your test under Java 14 with preview enabled and printing the empObj gives Emp[name=bob].

JDKs used,

  1. openjdk-14.0.2_linux-x64_bin
  2. openjdk-15_linux-x64_bin

This is due to changes in Java 15 that makes final fields in records notmodifiable via reflection. More information can be found here:

  1. (15) RFR: JDK-8247444: Trust final fields in records
  2. JDK-8247444 - Trust final fields in records

The relevant part on handling them going forward:

This change impacts 3rd-party frameworks including 3rd-party
serialization framework that rely on core reflection setAccessible or
sun.misc.Unsafe::allocateInstance and objectFieldOffset etc to
construct records but not using the canonical constructor.
These frameworks would need to be updated to construct records via its
canonical constructor as done by the Java serialization.

I see this change gives a good opportunity to engage the maintainers of
the serialization frameworks and work together to support new features
including records, inline classes and the new serialization mechanism
and which I think it is worth the investment.

A current workaround is to write a Deserializers that uses the records constructor instead of reflection.

>All comments

The problem seems to exist only in Java 15 with preview enabled. Running your test under Java 14 with preview enabled and printing the empObj gives Emp[name=bob].

JDKs used,

  1. openjdk-14.0.2_linux-x64_bin
  2. openjdk-15_linux-x64_bin

This is due to changes in Java 15 that makes final fields in records notmodifiable via reflection. More information can be found here:

  1. (15) RFR: JDK-8247444: Trust final fields in records
  2. JDK-8247444 - Trust final fields in records

The relevant part on handling them going forward:

This change impacts 3rd-party frameworks including 3rd-party
serialization framework that rely on core reflection setAccessible or
sun.misc.Unsafe::allocateInstance and objectFieldOffset etc to
construct records but not using the canonical constructor.
These frameworks would need to be updated to construct records via its
canonical constructor as done by the Java serialization.

I see this change gives a good opportunity to engage the maintainers of
the serialization frameworks and work together to support new features
including records, inline classes and the new serialization mechanism
and which I think it is worth the investment.

A current workaround is to write a Deserializers that uses the records constructor instead of reflection.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

GoogleCodeExporter picture GoogleCodeExporter  路  14Comments

danieleguiducci picture danieleguiducci  路  34Comments

priyankajagtap18 picture priyankajagtap18  路  14Comments

RobMans426 picture RobMans426  路  20Comments

cayhorstmann picture cayhorstmann  路  13Comments