【问题标题】:RuntimeException in Gson parsing JSON: Failed to invoke protected java.lang.ClassLoader() with no argsGson 解析 JSON 中的 RuntimeException:无法在没有参数的情况下调用受保护的 java.lang.ClassLoader()
【发布时间】:2012-12-06 07:56:12
【问题描述】:

我继承了一些代码,使用 Gson 将我们的应用程序状态保存为 JSON,然后使用 fromJson 读取它。

Gson gson = createGson();
gson.fromJson(objString, myClass);

要保存的字段之一是Location。不幸的是,由于我保存的位置在其 mExtras 中包含一个 mClassLoader,并且 Gson 库无法创建 ClassLoader 并出现此错误,因此该保存数据的解析偶尔会失败:

RuntimeException: 无法在没有参数的情况下调用受保护的 java.lang.ClassLoader()

有人知道为什么 ClassLoader 会包含在我的 Location 的 extras 中,以及它是否应该以 JSON 表示形式结束?

我假设我可以通过单独保存 Location 对象中的关键字段(例如经度、纬度、高度、时间、准确性)来解决此问题,但如果可能的话,最好保存 Location 对象。

我看到有一个 ExclusionStrategy 对象我可以用来排除字段,但我不确定我是否可以/应该使用它来排除我位置内部的额外内容...

仅供参考,这是我的 Location 对象的 JSON 数据(更改了经度和纬度以隐藏我):

{
    <snip>
    "lastKnownLocation": {
        "mResults": [
            0,
            0
        ],
        "mProvider": "gps",
        "mExtras": {
            "mParcelledData": {
                "mOwnObject": 1,
                "mObject": 5525040
            },
            "mClassLoader": {
                "packages": {}
            },
            "mMap": {},
            "mHasFds": false,
            "mFdsKnown": true,
            "mAllowFds": true
        },
        "mDistance": 0,
        "mTime": 1354658984849,
        "mAltitude": 5.199999809265137,
        "mLongitude": -122.4376,
        "mLon2": 0,
        "mLon1": 0,
        "mLatitude": 37.7577,
        "mLat1": 0,
        "mLat2": 0,
        "mInitialBearing": 0,
        "mHasSpeed": true,
        "mHasBearing": false,
        "mHasAltitude": true,
        "mHasAccuracy": true,
        "mAccuracy": 16,
        "mSpeed": 0,
        "mBearing": 0
    },
    <snip>
}

以下是代码不崩溃时 mExtras 包含的示例:

"mExtras": {
    "mParcelledData": {
        "mOwnsNativeParcelObject": true,
        "mNativePtr": 1544474480
    },
    "mHasFds": false,
    "mFdsKnown": true,
    "mAllowFds": true
}

【问题讨论】:

    标签: android location gson


    【解决方案1】:

    问题是您正试图将系统提供的类 (Location) 直接转换为 JSON。而且,正如您所见,您在序列化内部状态/Java 特定事物时遇到了问题。 JSON 是一种传递信息的半通用方式。

    你不能轻易使用@Expose 注解,因为它不是你的类;这将需要修改Location 的源代码,或者通过在运行时使用jassist 添加它们的相当广泛的过程(参见:http://ayoubelabbassi.blogspot.com/2011/01/how-to-add-annotations-at-runtime-to.html

    查看Location 类,我只需创建一个自定义Gson 序列化器和反序列化器并完成它。您真正感兴趣的是 GPS 数据,而不是课程本身的内部结构。您只需使用 getter 在序列化器中构造包含所需信息的 JSON,然后在反序列化器中创建 Location 的新实例,并使用公共设置器使用提供的 JSON 中的信息填充它。

    class LocationSerializer implements JsonSerializer<Location>
    {
        public JsonElement serialize(Location t, Type type, 
                                     JsonSerializationContext jsc)
        {
            JsonObject jo = new JsonObject();
            jo.addProperty("mProvider", t.getProvider());
            jo.addProperty("mAccuracy", t.getAccuracy());
            // etc for all the publicly available getters
            // for the information you're interested in
            // ...
            return jo;
        }
    
    }
    
    class LocationDeserializer implements JsonDeserializer<Location>
    {
        public Location deserialize(JsonElement je, Type type, 
                                    JsonDeserializationContext jdc)
                               throws JsonParseException
        {
            JsonObject jo = je.getAsJsonObject();
            Location l = new Location(jo.getAsJsonPrimitive("mProvider").getAsString());
            l.setAccuracy(jo.getAsJsonPrimitive("mAccuracy").getAsFloat());
            // etc, getting and setting all the data
            return l;
        }
    }
    

    现在在您的代码中使用GsonBuilder 并注册类:

    ...
    GsonBuilder gsonBuilder = new GsonBuilder();
    gsonBuilder.registerTypeAdapter(Location.class, new LocationDeserializer());
    gsonBuilder.registerTypeAdapter(Location.class, new LocationSerializer());
    Gson gson = gsonBuilder.create(); 
    ...
    

    这应该很照顾它。

    【讨论】:

    • 很好的答案,谢谢。我以为我检查了 Location 对象实现了 Serializable,但事实证明它没有 - 所以我们的代码被破坏也就不足为奇了!
    • 现已测试,修复效果很好。通过保存与系统使用的名称相同的字段名称(“mProvider”、“mAccuracy”等),它与包含用于引发异常的 mClassLoader extra 的旧应用程序数据向后兼容。
    • 太棒了。很高兴我能帮上忙。
    • 如果我想将 List 转换为 JSON,这种方法是否有效?因为 jo.getAsJsonPrimitive("objectId") 给了我 NullPointerException。
    【解决方案2】:

    考虑创建一个自定义对象来模拟您的 GSON,解析每个对象的内容对象和每个字段的字段

    类似

    JSONObject lastKnownLocation = obj.getJSONObject("lastKnownLocation");
    JSONArray mResults = lastKnownLocation.getJSONArray("mResults");
    
    etc...
    MyGSON mg=new MyGSON(lastKnownLocation, mResults etc....);
    

    因此您可以获得对解析的完全控制,并在关键的 mExtra 部分添加 try\catch 块,您可以轻松地排除该块,或根据需要管理异常。

    【讨论】:

      【解决方案3】:

      我想还有更简单的方法,但可能是错误的。

      您可以通过以下方式删除有问题的 Extras:

      myClass.loc.setExtras(null);

      • 其中 myClass.loc 是一个位置

      当我保存位置时它确实有效:

      initPrefs(); 
      mPrefs.edit().putFloat(PARAM_DISTANCE, mOdometer.getDistance()).apply();
      Location loc = mOdometer.getOrigLocation(); 
      loc.setExtras(null); 
      Gson gson = new Gson(); 
      String json = gson.toJson(loc); 
      mPrefs.edit().putString(PARAM_ORIG_LOCATION, json).apply();
      

      否则,我在阅读位置时遇到了与您相同的错误。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-06-02
        • 2012-03-27
        • 2016-09-08
        • 2022-01-21
        相关资源
        最近更新 更多