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
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?
Most helpful comment
That worked, thanks a lot. For the record: my kotlin-code: