安卓 - json-smart 反序列化问题

4
我在Android代码中遇到了一个奇怪的问题,当我试图将json-smart的JSONObjectJSONArray作为Intent序列化额外数据进行传递时。Json-smart是非常快速和轻量级的JSON解析器实现,我强烈推荐使用它。JSONObject扩展了HashMap<String,Object>,而JSONArray则扩展了带有很少开销的ArrayList<Object>。这些对象都没有覆盖Object#writeObject#read

问题出在这里:

如果我在Fragment#onSaveInstanceState(String, JSONObject)中使用这些对象,则一切正常。如果我在普通的Java示例项目中对这些对象进行序列化/反序列化,也能按预期工作。然而,如果我使用Intent#putExtra(String, JSONObject),然后尝试通过执行以下操作来获取我的JSONObject:

JSONObject json = (JSONObject) intent.getSerializableExtra("JSON");

我会得到ClassCastException,因为该方法返回的是普通的HashMap(在JSONArray的情况下是ArrayList)。此外,如果我查看映射/数组内部,内容完全被剥离了任何对JSONObject/JSONArray的引用,并替换为HashMaps/ArrayLists。
提交了工单并提供了示例项目,但不幸的是作者关闭了它而没有解决方案,所以我只是试图找到问题的根源。如果您转到工单,它附有简单的项目。
有没有办法摆脱这个问题?目前,我必须将JSONObject或JSONArray转换为字符串,并重新解析为对象,例如:
JSONArray feeds = (JSONArray) JSONValue.parse(intent.getStringExtra(RAW_FEED));

1
顺便提一下,JSONObject json = new JSONObject(intent.getSerializableExtra("JSON")) 应该可以正常工作。 - Jens
@Jens 这个不起作用(至少不完全)。是的 - 你会得到外部 JSONObject,但所有内部结构都将是普通的 HashMaps 和 ArrayLists。我刚刚通过修改和执行包含在票证中的测试项目来验证了这一点。 - Bostone
没错。JSON对象可以从HashMap构建,实际上就是一个HashMap。 - Konstantin Pribluda
只是一个问题,JSONSmart在两侧是否都有相同的版本可用?这是最常见的[反]序列化错误的来源。 - Konstantin Pribluda
问题清楚地概述了HashMap/ArrayList对象的性质。你所说的“两边”是什么意思?该库以jar文件的形式提供。附加到工单的是一个小型测试项目,欢迎您尝试任何组合。 - Bostone
3个回答

3

实现android.os.Parcelable:

http://developer.android.com/reference/android/os/Parcelable.html

基本上,您需要在对象中添加一些额外的代码,以解释如何对其进行序列化和反序列化。这就是为什么它丢失了您的类信息的原因,它不知道如何正确地对其进行序列化。如果我是你,我会通过意图传递值对象而不是JSON对象,因为现在您知道您要传递的对象的确切类型。您可以通过以下方式检索Parcelable:

getIntent().getExtras().getParcelable(key); 

我知道这似乎多了一点代码,但我认为这是在活动之间共享数据的一种非常干净的方式。你正在传递强类型,指定如何压缩/解压它,而且你的活动实际上不需要彼此有任何工作知识。
在您的值对象中,只需像这样填写空白:
/** Parcelable implementation: deserialize object
 * @param in Parcel object containing the data for this object
 * @return a Person object  */
public Person( Parcel in ) {
    String[] data = new String[2];
    in.readStringArray(data);
    this.firstName = data[0];
    this.lastName = data[1];
}

/** Parcelable implementation code */
public int describeContents() {
    return 0;
}

/** Parcelable implementation: Serialize object to be passed between activities via Intent
 * @param dest The parcel to transport the object
 * @param flags refer to Parcelable api
 * @return nothing */
public void writeToParcel(Parcel dest, int flags) {
    dest.writeStringArray( new String[] {
            this.firstName,
            this.lastName,      
    });
}

希望这能帮到你,可能比你想要的代码多一些,但这是一个真正干净的解决方案,因为当目标活动获取对象时,你不必担心反序列化,这意味着在你的活动中有更干净的代码。 大模型,清洁视图 :)

我正在尝试不增强JSON对象,而是使用“原样”。我对反序列化问题感到困惑,并希望了解为什么在这种特定情况下Android会剥离对象类型。 - Bostone
当你实现Parcelable时,你告诉Android如何序列化和反序列化你的对象,而不会丢失任何信息,例如类型。这就是你的问题所在,当你传递类似于我的Person例子的JSONObject类时,Android不知道如何处理它。如果你想以一种通用的方式传递JSON对象,请尝试创建一个值对象,它有一个类型为JSONObject的属性作为有效载荷。在该值对象上实现Parcelable。这将为你提供一个传输对象来发送你的JSON对象。这更像是一个设计建议,但应该可行 :) - jerrylroberts
我不确定这样做是否比转换/解析普通字符串更有效。 - Bostone
好的,你可以将它作为原始字符串传递。你的问题是,当你通过intent传递时,为什么它会丢失类类型。答案是,复杂类型应该作为Parcelabe对象传递。我认为没有人会对这个事实提出异议。如果你不喜欢这个答案,很抱歉,我只是想帮助你,或许澄清一下为什么这是首选方法。祝你好运 :) - jerrylroberts
这也意味着我将不得不编写Parcel例程,这可能最终将成为相同的字符串序列化,但需要复杂的解析逻辑(或将整个内容转换为字符串)。因此,我认为我在这里遇到了瓶颈,由于没有提供简单的答案,我将不得不像现在一样使用字符串序列化。感谢您标记所有这些有价值的观点,我不确定您的答案是否有资格获得完整的悬赏金,但我肯定会提高它。 - Bostone
显示剩余5条评论

1

由于JSON对象隐式地是一个字符串,为什么不直接将其作为字符串传递呢?

// save the json string
JSONObject json = ...
intent.putExtra("json", json.toString());


// Let's read it in in the next activity
JSONObject json = new JSONObject(intent.getExtra("json"));

这就是我所做的。但这需要将字符串重新解析为对象,而且代价惊人。 - Bostone
还请查看我的回答(对自己的问题) - Bostone

0
如果你正在阅读这篇文章,也许在疑惑我最终采用了哪种解决方案。实际上,我发现可以愉快地使用精简版的基于Map/List的对象,因为它保留了结构和所有键值对,无需再纠缠于Parcelable。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接