Gson: GsonBuilder.disableUnsafe

Created on 19 Mar 2015  Â·  15Comments  Â·  Source: google/gson

While it is nice to know that Unsafe is only used when no-arg ctors or instance 
creators are not present, it would be better to have a means to enforce it 
cannot be used.  Particularly in google app engine, I'd like to know that the 
code I use offline is safe inside GAE, rather than a delayed 
UnsupportedOperationException

Can you add GsonBuilder.disableUnsafe or similar?

Original issue reported on code.google.com by [email protected] on 16 May 2012 at 5:58

Most helpful comment

That worked, thanks a lot. For the record: my kotlin-code:

/* throw when no default-constructor was found */
object WarnConstructorFactory : TypeAdapterFactory {

    private val wrappers = listOf(
        java.lang.Boolean::class.java,
        java.lang.Character::class.java,
        java.lang.String::class.java,
        java.lang.Byte::class.java,
        java.lang.Short::class.java,
        java.lang.Integer::class.java,
        java.lang.Long::class.java,
        java.lang.Float::class.java,
        java.lang.Double::class.java
    )

    override fun <T> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? {

        val rawType = type.rawType
        if (!(rawType in wrappers || rawType.isInterface || 
              rawType.isPrimitive || rawType.isEnum)) {

            check(
                rawType.constructors.any { it.parameterTypes.count() == 0 },
                { "$type has no public no-args constructor" }
            )
        }
        return null;
    }
}

All 15 comments

I think this is a good idea. I personally don't like the unpredictability that 
UnsafeAllocator brings either.

Original comment by inder123 on 30 May 2012 at 4:17

+1

I was bitten by this too. I use kotlin with it's null-safety.

When I forget to add a default-value for a constructor, gson uses it's unsafe allocator. Afterwards, when I access a guaranteed-not-null value in kotlin, I get a java NPE without notice.

Would a PR for this be accepted? I'm thinkin about switching to Jackson solely because of this issue :(

You want Gson to crash when a class lacks a no-args constructor?

Yes. I use kotlin's data-classes. If I supply default-values for all constructor parameters, a no-args constructor is generated and everything works fine. If I forget a default-value somewhere though, gson initializes the class with all fields set to null, which then crash with a NPE when I access them, even though kotlin "guarantees" them to be not-null.

So I need gson to crash to see that I forgot to set the parameter.

Got it. It's possible to write a TypeAdapterFactory that does this. It'd delegate in the happy case and crash in the unhappy case.

How would I do that?

I have another problem related to Kotlin: if you deserialize a class with property delegates with gson, it won't set the necessary internal field and will fail at Runtime: https://discuss.kotlinlang.org/t/npe-with-delegate/1808/3

Should I open another bug for this?

@swankjesse Is there any news on this, or could you provide a hint on how to write a general TypeAdapterFactory for this?

I already use a TypeAdapterFactory that serializes abstract types using their classnames.

I've been bitten countless times by gson's Unsafe by now, especially when it doesn't call an Abstract super constructor and fails to set fields need for delegated properties :(.

Having at least a warning that there's no constructor would save me a lot of headache…

Your TypeAdapterFactory would look for the no args constructor. If absent it would throw. Otherwise it would delegate to the next type adapter.

That worked, thanks a lot. For the record: my kotlin-code:

/* throw when no default-constructor was found */
object WarnConstructorFactory : TypeAdapterFactory {

    private val wrappers = listOf(
        java.lang.Boolean::class.java,
        java.lang.Character::class.java,
        java.lang.String::class.java,
        java.lang.Byte::class.java,
        java.lang.Short::class.java,
        java.lang.Integer::class.java,
        java.lang.Long::class.java,
        java.lang.Float::class.java,
        java.lang.Double::class.java
    )

    override fun <T> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? {

        val rawType = type.rawType
        if (!(rawType in wrappers || rawType.isInterface || 
              rawType.isPrimitive || rawType.isEnum)) {

            check(
                rawType.constructors.any { it.parameterTypes.count() == 0 },
                { "$type has no public no-args constructor" }
            )
        }
        return null;
    }
}

@swankjesse One additional question, if you have time: Is it possible that the Unsafe-allocator doesn't initialize transient fields like this one:

@Transient String test = "Test"

It is null after having deserialized the class, even though it's initialized.

Yep. Workaround this by adding a no-args constructor.

@fab1an I think your test is missing || rawType.isArray.

There are still StrictMode policy violation: android.os.strictmode.NonSdkApiUsedViolation: Lsun/misc/Unsafe;->theUnsafe:Lsun/misc/Unsafe; with current 2.8.5.

We can leverage type adapters to prevent this situation, but it’s an approach that can easily lead to a lot of custom deserialization and hard to follow. In big projects, this is quite undesirable.

Is there any other solution that we can look upon which also works well with Kotlin NPE too?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

GoogleCodeExporter picture GoogleCodeExporter  Â·  32Comments

ominfowave picture ominfowave  Â·  13Comments

JakeWharton picture JakeWharton  Â·  39Comments

GoogleCodeExporter picture GoogleCodeExporter  Â·  19Comments

GoogleCodeExporter picture GoogleCodeExporter  Â·  15Comments