【问题标题】:Android - json-smart deserialization issueAndroid - json-smart 反序列化问题
【发布时间】:2011-12-09 18:25:19
【问题描述】:

当我尝试将 json-smart JSONObjectJSONArray 作为可序列化数据作为 Intent 可序列化额外数据传递时,我的 Android 代码中有一个奇怪的问题。 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

filed the ticket 并提供了示例项目,但不幸的是作者没有解决就关闭了它,所以我只是想深入了解它。如果你去买票,它附有简单的项目。

有办法解决吗?现在我必须将 JSONObject 或 JSONArray 转换为 String 并将其重新解析回对象,例如:

JSONArray feeds = (JSONArray) JSONValue.parse(intent.getStringExtra(RAW_FEED));

【问题讨论】:

  • 仅供参考,JSONObject json = new JSONObject(intent.getSerializableExtra("JSON")) 应该可以正常工作。
  • @Jens 这不起作用(至少完全不起作用)。是的 - 您将获得外部 JSONObject,但所有内部结构都是普通的 HashMaps 和 ArrayLists。我刚刚通过修改和执行票证中包含的测试项目验证了这一点
  • 是的。 JSON Object 可以由 HashMap 构造,实际上是一个 HashMap。
  • 只是一个问题,JSONSmart 是否在同一版本中两边都可用?这是 [反] 序列化事故的最常见来源
  • 问题清楚地概述了对象的 HashMap/ArrayList 性质。 “双方”是什么意思?库以 jar 形式提供。门票附有小测试项目,欢迎尝试任意组合。

标签: android json


【解决方案1】:

实现 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 在这种特殊情况下会去除对象类型
  • 当你实现 Parcelable 时,你是在告诉 Android 如何序列化和反序列化你的对象而不丢失任何信息,例如类型。这就是您的问题所在,当您传递 JSONObject(就像我的 Person 示例一样是一个类)时,Android 不知道如何处理它。如果您想要一种通用的方式来传递 JSON 对象,请尝试创建一个值对象,该对象具有一个 JSONObject 类型的属性用于负载。在该值对象上实现 Parcelable。这将为您提供一个传输对象来发送您的 JSON 对象。更多的设计建议,但它应该工作:)
  • 我不确定这样做会比转换/解析普通字符串更有效
  • 很公平,您可以简单地将其作为原始字符串传递。您的问题是,为什么当您通过意图时它会丢失类类型。答案是,复杂类型是作为 Parcelabe 对象传递的。我认为没有人会质疑这个事实。抱歉,如果您不喜欢该答案,只是想提供帮助,也许可以确切说明为什么这是首选方法。祝你好运:)
  • 这也意味着我必须编写 Parcel 例程,这些例程可能最终会是相同的字符串序列化,但需要复杂的解析逻辑(或将整个内容转换为字符串)。所以我认为我在这里遇到了死胡同,由于没有提供简单的答案,我将不得不像现在一样使用字符串序列化。感谢您标记所有这些宝贵的点,我不确定您的答案是否有资格获得全部赏金,但我肯定会提高它
【解决方案2】:

作为一个 JSON-Object 隐含地是一个字符串,为什么不简单地将它作为字符串传递呢?

// 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"));

【讨论】:

  • 我就是这么做的。但这需要将 String 重新解析为对象,而且成本高得惊人
  • 还可以查看我的回答(对我自己的问题)
【解决方案3】:

如果您阅读本文并想知道我最终实现了哪个解决方案,我意识到我可以愉快地使用基于 Map/List 的对象的精简版本,因为它保留了结构和所有键。无需使用 Parcelable 进行猴子

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-23
    • 1970-01-01
    • 2021-05-28
    • 1970-01-01
    相关资源
    最近更新 更多