为什么缩小会导致错误 "JSONObject["rewards"] not a string"?
Why does minify cause error "JSONObject["rewards"] not a string"?
不知何故,从一个应用程序发布到另一个应用程序,org.json 库的内部结构损坏了。它无法再将键的内容作为字符串读取。以下是启用 minify/R8/proguard 时曾经有效但正在中断的代码示例。
try {
parseJson("{\"rewards\":[{\"amount\":1,\"name\":\"Add Audio\"}]}");
} catch (Exception e) {
Log.w("TEST", "Error parsing rewarded currencies JSON header!!!", e);
}
private void parseJson(@NonNull String rewardedCurrencies) throws JSONException {
final Map<String, String> rewardsMap = jsonStringToMap(rewardedCurrencies);
for (Map.Entry<String, String> entry : rewardsMap.entrySet()) {
Log.e("TEST", entry.getKey() + ":" + entry.getValue());
}
}
public Map<String, String> jsonStringToMap(String jsonParams) throws JSONException {
Map<String, String> jsonMap = new HashMap<String, String>();
if (TextUtils.isEmpty(jsonParams)) return jsonMap;
JSONObject jsonObject = (JSONObject) new JSONTokener(jsonParams).nextValue();
Iterator<String> keys = jsonObject.keys();
Log.e("TEST", "jsonStringToMap() -> jsonObject: "+jsonObject.toString());
while (keys.hasNext()) {
String key = (String) keys.next();
Log.e("TEST", "jsonStringToMap() -> key: "+key);
jsonMap.put(key, jsonObject.getString(key));
}
return jsonMap;
}
失败的日志输出:
jsonStringToMap() -> jsonObject: {"rewards":[{"amount":1,"name":"Add Audio"}]}
jsonStringToMap() -> key: rewards
Error parsing rewarded currencies JSON header!!!
j.b.b: JSONObject["rewards"] not a string.
at j.b.d.getString(SourceFile:4)
自上次发布可运行的应用程序以来,项目发生了哪些变化?
targetSdkVersion 28 -> 29
sourceCompatibility JavaVersion.VERSION_1_7 -> JavaVersion.VERSION_1_8
'com.android.tools.build:gradle:4.0.0' -> 'com.android.tools.build:gradle:4.1.1'
distributionUrl ->
gradle-6.1.1-all.zip -> gradle-6.5-all.zip
修复方法是向混淆器脚本应用一个新行。
-keep class org.json.** { *; }
现在我很担心。为什么我需要将其应用于混淆器?是否有其他内部库可能会破坏?我似乎无法弄清楚是什么原因造成的,这是否是一个潜在的大问题?
之所以会发生此问题,是因为当您将 r8 收缩器应用于 Android 应用时,它会重命名 class 中定义的一些内部字段和方法。如果这些 methods/fields 是代码中其他地方的引用,那么它将具有未定义/意外的行为。要解决此问题,请尝试在混淆文件中添加此规则,这将阻止 R8 重命名公开引用的字段和方法
-keep class org.json.** { *; }
我能找到的关于错误日志中异常文本的唯一参考来自这里:JSONObject not a String Error
我无法从 Android org.json 实现中找到会导致该异常的代码路径。这意味着 org.json
以某种方式被打包到您的应用程序中。这也意味着它不是您担心的标准库问题。
进一步强调这一点:https://github.com/stleary/JSON-java/blob/master/src/main/java/org/json/JSONObject.java#L849-L864
这完全解释了您的问题。 JSONObject#getString(String key)
的实现不同于 Android 的实现。您只能通过这种方式读取对象的字符串值。任何其他值都将被拒绝并导致异常。由于您正在阅读 JSONArray
这将不起作用。
无论您使用的是 Android 标准库还是拉取的依赖项,我下面的示例都会解决这个问题。
它在没有 ProGuard 的情况下工作的原因是系统已经提供了 Androids org.json。缩小时,它会重命名依赖项 org.json 包,从而强制您使用它。
我建议摆脱 org.json
依赖。或者编写适用于两者的代码...您可能想要切换到 Gson
或 Jackson
,因为它更容易使用。
public static Map<String, String> jsonStringToMap(String jsonParams) throws JSONException {
Map<String, String> jsonMap = new HashMap<>();
if (TextUtils.isEmpty(jsonParams)) return jsonMap;
// Use JSONObject constructor
JSONObject jsonObject = new JSONObject(jsonParams);
Iterator<String> keys = jsonObject.keys();
Log.e("TEST", "jsonStringToMap() -> jsonObject: " + jsonObject.toString());
while (keys.hasNext()) {
String key = keys.next();
Log.e("TEST", "jsonStringToMap() -> key: " + key);
// Type safe way to get the String value of any type within the key
// Probably needs to handle null values correctly
jsonMap.put(key, String.valueOf(jsonObject.get(key)));
}
return jsonMap;
}
不知何故,从一个应用程序发布到另一个应用程序,org.json 库的内部结构损坏了。它无法再将键的内容作为字符串读取。以下是启用 minify/R8/proguard 时曾经有效但正在中断的代码示例。
try {
parseJson("{\"rewards\":[{\"amount\":1,\"name\":\"Add Audio\"}]}");
} catch (Exception e) {
Log.w("TEST", "Error parsing rewarded currencies JSON header!!!", e);
}
private void parseJson(@NonNull String rewardedCurrencies) throws JSONException {
final Map<String, String> rewardsMap = jsonStringToMap(rewardedCurrencies);
for (Map.Entry<String, String> entry : rewardsMap.entrySet()) {
Log.e("TEST", entry.getKey() + ":" + entry.getValue());
}
}
public Map<String, String> jsonStringToMap(String jsonParams) throws JSONException {
Map<String, String> jsonMap = new HashMap<String, String>();
if (TextUtils.isEmpty(jsonParams)) return jsonMap;
JSONObject jsonObject = (JSONObject) new JSONTokener(jsonParams).nextValue();
Iterator<String> keys = jsonObject.keys();
Log.e("TEST", "jsonStringToMap() -> jsonObject: "+jsonObject.toString());
while (keys.hasNext()) {
String key = (String) keys.next();
Log.e("TEST", "jsonStringToMap() -> key: "+key);
jsonMap.put(key, jsonObject.getString(key));
}
return jsonMap;
}
失败的日志输出:
jsonStringToMap() -> jsonObject: {"rewards":[{"amount":1,"name":"Add Audio"}]}
jsonStringToMap() -> key: rewards
Error parsing rewarded currencies JSON header!!!
j.b.b: JSONObject["rewards"] not a string.
at j.b.d.getString(SourceFile:4)
自上次发布可运行的应用程序以来,项目发生了哪些变化?
targetSdkVersion 28 -> 29
sourceCompatibility JavaVersion.VERSION_1_7 -> JavaVersion.VERSION_1_8
'com.android.tools.build:gradle:4.0.0' -> 'com.android.tools.build:gradle:4.1.1'
distributionUrl -> gradle-6.1.1-all.zip -> gradle-6.5-all.zip
修复方法是向混淆器脚本应用一个新行。
-keep class org.json.** { *; }
现在我很担心。为什么我需要将其应用于混淆器?是否有其他内部库可能会破坏?我似乎无法弄清楚是什么原因造成的,这是否是一个潜在的大问题?
之所以会发生此问题,是因为当您将 r8 收缩器应用于 Android 应用时,它会重命名 class 中定义的一些内部字段和方法。如果这些 methods/fields 是代码中其他地方的引用,那么它将具有未定义/意外的行为。要解决此问题,请尝试在混淆文件中添加此规则,这将阻止 R8 重命名公开引用的字段和方法
-keep class org.json.** { *; }
我能找到的关于错误日志中异常文本的唯一参考来自这里:JSONObject not a String Error
我无法从 Android org.json 实现中找到会导致该异常的代码路径。这意味着 org.json
以某种方式被打包到您的应用程序中。这也意味着它不是您担心的标准库问题。
进一步强调这一点:https://github.com/stleary/JSON-java/blob/master/src/main/java/org/json/JSONObject.java#L849-L864
这完全解释了您的问题。 JSONObject#getString(String key)
的实现不同于 Android 的实现。您只能通过这种方式读取对象的字符串值。任何其他值都将被拒绝并导致异常。由于您正在阅读 JSONArray
这将不起作用。
无论您使用的是 Android 标准库还是拉取的依赖项,我下面的示例都会解决这个问题。
它在没有 ProGuard 的情况下工作的原因是系统已经提供了 Androids org.json。缩小时,它会重命名依赖项 org.json 包,从而强制您使用它。
我建议摆脱 org.json
依赖。或者编写适用于两者的代码...您可能想要切换到 Gson
或 Jackson
,因为它更容易使用。
public static Map<String, String> jsonStringToMap(String jsonParams) throws JSONException {
Map<String, String> jsonMap = new HashMap<>();
if (TextUtils.isEmpty(jsonParams)) return jsonMap;
// Use JSONObject constructor
JSONObject jsonObject = new JSONObject(jsonParams);
Iterator<String> keys = jsonObject.keys();
Log.e("TEST", "jsonStringToMap() -> jsonObject: " + jsonObject.toString());
while (keys.hasNext()) {
String key = keys.next();
Log.e("TEST", "jsonStringToMap() -> key: " + key);
// Type safe way to get the String value of any type within the key
// Probably needs to handle null values correctly
jsonMap.put(key, String.valueOf(jsonObject.get(key)));
}
return jsonMap;
}