Android,Gson擦除ArrayList内部数据<HashMap<String,Uri>>

问题描述 投票:0回答:2

我尝试使用 Gson 将 ArrayList> 保存到 json 中,但是当我从 json 读取数据时,我注意到 Gson 只是擦除了 HashMap 中的数据。

我希望 Gson 不再擦除我在 HashMap 中的数据。

这是我在 ConfigurationActivity 中的保存代码:

 endPlanConfiguration.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Intent intent = new Intent(v.getContext(), MainActivity.class);
            Gson gson = new Gson();
            String arraylist =  gson.toJson(Singleton.getInstance(v.getContext()).getWorkoutPathList());
           SharedPreferences  prefs = getSharedPreferences("Workout", MODE_PRIVATE);
            SharedPreferences.Editor editor = prefs.edit();
            editor.putString("WorkoutPathList", arraylist);
            editor.apply();
            startActivity(intent);
            finish();

        }
    });

这是我的 MainActivity 中的代码:

抱歉这个列表,这是多次尝试重写代码的结果。

 public void loadRecView(){
            try {
                   SharedPreferences sharedPreferences = getSharedPreferences("Workout", MODE_PRIVATE);
                    Gson gson = new Gson();
                    String json = sharedPreferences.getString("WorkoutPathList", null);
                workoutList = gson.fromJson(json, new TypeToken<ArrayList<HashMap<String, Uri>>>() {
                }.getType());

            recyclerView.setAdapter(new AdapterToDays(MainActivity.this, workoutList));

            }
            catch (ClassCastException | NullPointerException ex){
                Toast.makeText(MainActivity.this, "Определите план тренировок!", Toast.LENGTH_SHORT).show();
            } 
}
java android json gson
2个回答
0
投票

您可以创建这样的模型,

public class MyModel {

    private String name;
    private Uri uri;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Uri getUri() {
        return uri;
    }

    public void setUri(Uri uri) {
        this.uri = uri;
    }
}

您的

HashMap<String, Uri>
可以简化为
List<MyModel>
使用这个简化的列表作为外部列表中的类型。

ArrayList<HashMap<String, Uri>>
更改为
List<List<MyModel>>
,这样会减少混乱。

然后尝试将传入的数据分配到这个列表中,

SharedPreferences sharedPreferences = getSharedPreferences("Workout", MODE_PRIVATE);
String json = sharedPreferences.getString("WorkoutPathList", null);
List<List<MyModel>> workOutList = new Gson().fromJson(json, new TypeToken<List<List<MyModel>>>() {}.getType());

注意:Gson 转换不会修改/删除我们传递给它的数据。 Singleton 类中的数据检索可能存在问题。核实。另一件重要的事情是,您传递给 Gson 的数据不应该是空字符串,因为您使用了 try catch 块,所以您无法找出问题所在。尝试使用

Exception
而不是
ClassCastException | NullPointerException
,然后记录异常。你可能会得到一些线索。

如果您尚未解决问题,则必须尝试上述代码建议。

祝一切顺利。


0
投票

根本问题是您正在尝试使用 Gson 序列化

android.net.Uri
,但 Gson 没有内置适配器。您可以通过将
Uri
替换为具有内置 Gson 支持的类型(例如
String
)来解决此问题,或者为
TypeAdapter
编写自定义 Gson
Uri
,请参阅此相关问题。但是,您必须使用
GsonBuilder.registerTypeHierarchyAdapter(...)
注册适配器,因为您希望它也处理
Uri
的子类。


以下是对您遇到的问题的更深入的解释,以便您以后更轻松地排查 Gson 问题。其中一些是假设,可能略有不正确。

使用您的原始代码,地图为空且

GsonBuilder.serializeNulls()
使它们包含
null
值的原因是:

  • 默认情况下,Gson 不会序列化
    null
    ,只是省略 JSON 对象中的成员
  • 如果 Gson 遇到本地或匿名类,它会将它们序列化为
    null

    android.net.Uri
    是抽象的,因此可以想象,您正在序列化的实际对象是匿名类的实例。
    android.net.Uri
    的当前源代码实际上似乎并没有创建匿名类,但可能您正在使用的Android版本正在这样做,或者可能涉及到一些不同的东西,使Gson认为该类是本地或匿名的。

关于您更改代码后遇到的第二个问题:

抽象类无法实例化!为此类型注册 InstanceCreator 或 TypeAdapter。类名:android.net.Uri

如上所述,Gson 没有内置适配器

android.net.Uri

您在之前的代码中没有看到此异常的原因是,正如您提到的,JSON 中的映射是空的,因此 Gson 从未尝试构造
android.net.Uri

但是使用更改后的代码,Gson 将
Uri
序列化为非
null
值(请参阅下面的可能解释),因此现在也尝试再次反序列化它们。

关于第三个问题,从

Uri
切换到
String
后:

IllegalStateException:需要一个字符串,但在第 1 行第 45 列路径 $[0][0].uri 处是 BEGIN_OBJECT

此时,您可能已成功将

Uri
值序列化为 JSON,可能是因为当字段具有类型
Uri
时,Gson 不仅考虑了运行时类 (
getClass()
),它之前被序列化为
null
,还有字段
[1]
的编译时类Uri
但因为您还没有注册自定义适配器,Gson 使用基于反射的适配器并将
Uri
序列化为 JSON 对象 (
{ ... }
)[2]。因此,当您将
Uri
更改为
String
时,共享首选项中可能仍然有旧的 JSON,因此 Gson 尝试将
{ ... }
的先前 JSON 对象
Uri
序列化为 JSON 字符串
" ... "

要解决此问题,只需从共享首选项中删除旧条目即可。


[1]:更多详情请参阅Gson内部

TypeAdapterRuntimeTypeWrapper

[2]:应该避免依赖第三方类的基于反射的适配器,因为这样您就依赖于它们的实现细节,这些细节可能随时发生变化。

© www.soinside.com 2019 - 2024. All rights reserved.