-
Notifications
You must be signed in to change notification settings - Fork 4.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix failing to serialize Collection or Map with inaccessible constructor #1902
Changes from all commits
84afa36
9620747
3acc2e4
6819419
41ff3e3
0696e12
2dfccd2
9140460
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,6 +17,7 @@ | |
package com.google.gson.internal.bind; | ||
|
||
import java.io.IOException; | ||
import java.lang.reflect.AccessibleObject; | ||
import java.lang.reflect.Field; | ||
import java.math.BigDecimal; | ||
import java.math.BigInteger; | ||
|
@@ -759,22 +760,31 @@ private static final class EnumTypeAdapter<T extends Enum<T>> extends TypeAdapte | |
private final Map<String, T> nameToConstant = new HashMap<String, T>(); | ||
private final Map<T, String> constantToName = new HashMap<T, String>(); | ||
|
||
public EnumTypeAdapter(Class<T> classOfT) { | ||
public EnumTypeAdapter(final Class<T> classOfT) { | ||
try { | ||
for (final Field field : classOfT.getDeclaredFields()) { | ||
if (!field.isEnumConstant()) { | ||
continue; | ||
} | ||
AccessController.doPrivileged(new PrivilegedAction<Void>() { | ||
@Override public Void run() { | ||
field.setAccessible(true); | ||
return null; | ||
// Uses reflection to find enum constants to work around name mismatches for obfuscated classes | ||
// Reflection access might throw SecurityException, therefore run this in privileged context; | ||
// should be acceptable because this only retrieves enum constants, but does not expose anything else | ||
Field[] constantFields = AccessController.doPrivileged(new PrivilegedAction<Field[]>() { | ||
@Override public Field[] run() { | ||
Field[] fields = classOfT.getDeclaredFields(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Have changed this to also run Not directly related to the other changes of this pull request, but I was having a look at other cases where a |
||
ArrayList<Field> constantFieldsList = new ArrayList<Field>(fields.length); | ||
for (Field f : fields) { | ||
if (f.isEnumConstant()) { | ||
constantFieldsList.add(f); | ||
} | ||
} | ||
}); | ||
|
||
Field[] constantFields = constantFieldsList.toArray(new Field[0]); | ||
AccessibleObject.setAccessible(constantFields, true); | ||
return constantFields; | ||
} | ||
}); | ||
for (Field constantField : constantFields) { | ||
@SuppressWarnings("unchecked") | ||
T constant = (T)(field.get(null)); | ||
T constant = (T)(constantField.get(null)); | ||
String name = constant.name(); | ||
SerializedName annotation = field.getAnnotation(SerializedName.class); | ||
SerializedName annotation = constantField.getAnnotation(SerializedName.class); | ||
if (annotation != null) { | ||
name = annotation.value(); | ||
for (String alternate : annotation.alternate()) { | ||
|
This file was deleted.
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
package com.google.gson.internal.reflect; | ||
|
||
import com.google.gson.JsonIOException; | ||
import java.lang.reflect.Constructor; | ||
import java.lang.reflect.Field; | ||
|
||
public class ReflectionHelper { | ||
private ReflectionHelper() { } | ||
|
||
/** | ||
* Tries making the field accessible, wrapping any thrown exception in a | ||
* {@link JsonIOException} with descriptive message. | ||
* | ||
* @param field field to make accessible | ||
* @throws JsonIOException if making the field accessible fails | ||
*/ | ||
public static void makeAccessible(Field field) throws JsonIOException { | ||
try { | ||
field.setAccessible(true); | ||
} catch (Exception exception) { | ||
throw new JsonIOException("Failed making field '" + field.getDeclaringClass().getName() + "#" | ||
+ field.getName() + "' accessible; either change its visibility or write a custom " | ||
+ "TypeAdapter for its declaring type", exception); | ||
} | ||
} | ||
|
||
/** | ||
* Creates a string representation for a constructor. | ||
* E.g.: {@code java.lang.String#String(char[], int, int)} | ||
*/ | ||
private static String constructorToString(Constructor<?> constructor) { | ||
StringBuilder stringBuilder = new StringBuilder(constructor.getDeclaringClass().getName()) | ||
.append('#') | ||
.append(constructor.getDeclaringClass().getSimpleName()) | ||
.append('('); | ||
Class<?>[] parameters = constructor.getParameterTypes(); | ||
for (int i = 0; i < parameters.length; i++) { | ||
eamonnmcmanus marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if (i > 0) { | ||
stringBuilder.append(", "); | ||
} | ||
stringBuilder.append(parameters[i].getSimpleName()); | ||
} | ||
|
||
return stringBuilder.append(')').toString(); | ||
} | ||
|
||
/** | ||
* Tries making the constructor accessible, returning an exception message | ||
* if this fails. | ||
* | ||
* @param constructor constructor to make accessible | ||
* @return exception message; {@code null} if successful, non-{@code null} if | ||
* unsuccessful | ||
*/ | ||
public static String tryMakeAccessible(Constructor<?> constructor) { | ||
try { | ||
constructor.setAccessible(true); | ||
return null; | ||
} catch (Exception exception) { | ||
return "Failed making constructor '" + constructorToString(constructor) + "' accessible; " | ||
+ "either change its visibility or write a custom InstanceCreator or TypeAdapter for its declaring type: " | ||
// Include the message since it might contain more detailed information | ||
+ exception.getMessage(); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
JsonIOException
thrown here and inReflectionHelper
is not fitting very well, but Gson has currently no better fitting exception type.(maybe the whole Gson exception hierarchy should be refactored in the future)