首页 > 其他分享 >Android中使用Gson

Android中使用Gson

时间:2023-12-17 13:23:34浏览次数:40  
标签:0.0 new class 使用 Gson Android 序列化 public

Gson是一个Java库,可用于将Java对象转换为它们的JSON表示。它还可以用于将JSON字符串转换为等效的Java对象。Gson可以处理任意Java对象,包括您没有源代码的已有对象。

一、简单使用

1. 导入

在Android的build.gradle中添加依赖:

dependencies {
    implementation 'com.google.code.gson:gson:2.10.1'
}

2. Gson序列化

下面以一个简单例子展示对象序列化:

Item item = new Item();
item.id = 101;
item.name = "Apple";
Gson gson = new Gson();
String json = gson.toJson(item);
Log.d(TAG, json);

打印的json字符串值为:

{"id": 101,"name": "Apple"}

序列化就是如此简单,json字段名就是类的属性名,如果要修改序列化字段名,需要在类型属性上添加注解@SerializedName

public class Item {
    @SerializedName("ID")
    int id;
    String name;

    @NonNull
    @Override
    public String toString() {
        return "Item{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

那么json的字符串值将变为:

{"ID": 101,"name": "Apple"}

3. Gson对象解析

将上面的json直接解析为Item对象:

Item it = gson.fromJson(json, Item.class);
Log.d(TAG, it.toString());

打印结果如下:

Item{id=101, name='Apple'}

可见Gson的使用是非常简单的,掌握上述方法已经可以处理大部分JSON序列化问题了。

二、混淆

Gson的对象解析利用到了java反射机制,如果开启了混淆是否影响序列化和反序列化,接下来做一个简单的实验。
在build.gradle中打开混淆开关:

buildTypes {
    debug {
        minifyEnabled true
        proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
    }
}

再次运行上面的代码,将得到如下类似打印:

{"ID":101,"b":"Apple"}

由于在id字段上添加了注解序列化名称,因此ID名称并没有因混淆被影响;而name字段由于混淆名称变为了b,所以JSON字段名称也变成了b
所以要避免混淆对系列化的影响,一是可以在需要化字段上添加注解@SerializedName,二是防止整个类被混淆,如可在类上添加注解@Keep,或者在混淆文件中添加

-keepclasseswithmembernames class com.ihuntto.hellogson.Item {*;}

三、序列化部分类属性

上面给出的例子是对类的属性进行全部JSON序列化,如果要进行部分序列化,该怎么办?这里有多种方式可选择。

1. 使用transient修饰符

在不需要序列化字段前面添加transient

transient String nickName = "Pie";

2. 指定Modifier字段

如不序列化privateprotected字段:

Gson gson = new GsonBuilder().excludeFieldsWithModifiers(Modifier.PRIVATE, Modifier.PROTECTED).create();

当然还用其他Modifier可以选择:

package java.lang.reflect;
public class Modifier {
    public static final int ABSTRACT = 1024;
    public static final int FINAL = 16;
    public static final int INTERFACE = 512;
    public static final int NATIVE = 256;
    public static final int PRIVATE = 2;
    public static final int PROTECTED = 4;
    public static final int PUBLIC = 1;
    public static final int STATIC = 8;
    public static final int STRICT = 2048;
    public static final int SYNCHRONIZED = 32;
    public static final int TRANSIENT = 128;
    public static final int VOLATILE = 64;
    ...
}

3. 使用注解@Expose

在需要进行序列化的字段上添加@Expose注解:

public class Item {
    @Expose
    @SerializedName("ID")
    int id;

    @Expose
    String name;

    transient String nickName = "Pie";
    String otherStuff;
    ...  
}

使用GsonBuilder创建Gson,以排除不包含@Expose注解的字段:

Item item = new Item();
item.id = 101;
item.name = "Apple";
item.otherStuff = "Red";
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
String json = gson.toJson(item);
Log.d(TAG, json);

序列化的json字符串打印为:

{"ID":101,"name":"Apple"}

可见nickNameotherStuff字段都没有被序列化。
也可以指定字段只支持序列化或反序列化:

@Expose(serialize = true, deserialize = false)

4. 自定义排除策略

如果上面还不能满足部分字段序列化需求,还可以自定义排除策略:

public class MyExclusionStrategy implements ExclusionStrategy {
    @Override
    public boolean shouldSkipField(FieldAttributes field) {
        return field.getName().startsWith("_");
    }

    @Override
    public boolean shouldSkipClass(Class<?> clazz) {
        return clazz.isAnonymousClass();
    }
}

排除以下划线开头的字段,以及匿名内部类不进行序列化,还可以根据FieldAttributesClass<?>支持的判断添加排除策略。

综合上述4种方法,首先不推荐使用@Expose注解,因为任何库中类的字段不支持你添加@Expose注解的,也就是你不能序列化任何库中的类对象;其次不推荐使用Modifier进行排除序列化字段,一是代码规范基本建议不使用public字段,二是protectedprivate可以用来区别子类的可见性,但不是区分序列化的标志,如果实在是要使用,建议使用其他Modifier来区分;比较建议使用transient,因为java语言已经明确其修饰字段不进行序列化。

四、反序列化含子类的列表

如果在ArrayList<IShape>的列表中添加了IShape的子类,那么这个列表还能正常序列化和反序列化吗?

interface IShape {
    void draw();
}
public class Circle implements IShape {
    private static final String TAG = Circle.class.getSimpleName();
    float x;
    float y;
    float radius;

    public Circle() {
    }

    public Circle(float x, float y, float radius) {
        this.x = x;
        this.y = y;
        this.radius = radius;
    }

    @Override
    public void draw() {
        Log.d(TAG, "draw " + toString());
    }

    @NonNull
    @Override
    public String toString() {
        return "Circle{" +
                "x=" + x +
                ", y=" + y +
                ", radius=" + radius +
                '}';
    }
}
public class Rectangle implements IShape {
    private static final String TAG = Rectangle.class.getSimpleName();

    float x;
    float y;
    float width;
    float height;

    public Rectangle() {
    }

    public Rectangle(float x, float y, float width, float height) {
        this.x = x;
        this.y = y;
        this.width = width;
        this.height = height;
    }

    @Override
    public void draw() {
        Log.d(TAG, "draw " + toString());
    }

    @NonNull
    @Override
    public String toString() {
        return "Rectangle{" +
                "x=" + x +
                ", y=" + y +
                ", width=" + width +
                ", height=" + height +
                '}';
    }
}
public class ShapeHolder {
    List<IShape> shapes = new ArrayList<>();
}
IShape circle = new Circle(0, 0, 10);
IShape rectangle = new Rectangle(0, 0, 20, 10);
ShapeHolder holder = new ShapeHolder();
holder.shapes.add(circle);
holder.shapes.add(rectangle);
Gson gson = new Gson();
String json = gson.toJson(holder);
Log.d(TAG, json);

上述代码可以正常序列化:

{"shapes":[{"radius":10.0,"x":0.0,"y":0.0},{"height":10.0,"width":20.0,"x":0.0,"y":0.0}]}

但是将json字符串进行反序列化时:

ShapeHolder shapeHolder = gson.fromJson(json, ShapeHolder.class);

将得到如下异常:

com.google.gson.JsonIOException: Interfaces can't be instantiated! Register an InstanceCreator or a TypeAdapter for this type. Interface name: com.ihuntto.hellogson.IShape

异常提示注册一个InstanceCreator或者TypeAdapter,那么尝试为gson添加一个TypeAdapter,首先在CircleRectangle中添加type字段以区分不同的序列化类:

public class Circle implements IShape {
    ...
    String type = "circle";
    ...
}
public class Rectangle implements IShape {
    ...
    String type = "rectangle";
    ...
}
public class IShapeDeserializer implements JsonDeserializer<IShape> {
    private final Gson mGson = new Gson();
    private final HashMap<String, Class<? extends IShape>> mTypeMap = new HashMap<>();

    public IShapeDeserializer() {
        mTypeMap.put("circle", Circle.class);
        mTypeMap.put("rectangle", Rectangle.class);
    }

    @Override
    public IShape deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        Class<? extends IShape> clazz = mTypeMap.get(json.getAsJsonObject().get("type").getAsString());
        return mGson.fromJson(json, clazz);
    }
}

现在进行序列化与反序列化:

IShape circle = new Circle(0, 0, 10);
IShape rectangle = new Rectangle(0, 0, 20, 10);
ShapeHolder holder = new ShapeHolder();
holder.shapes.add(circle);
holder.shapes.add(rectangle);

Gson gson = new GsonBuilder().registerTypeAdapter(IShape.class, new IShapeDeserializer()).create();

String json = gson.toJson(holder);
Log.d(TAG, json);

ShapeHolder shapeHolder = gson.fromJson(json, ShapeHolder.class);
for (IShape shape : shapeHolder.shapes) {
    shape.draw();
}

运行上述代码将得到如下打印:

2023-12-17 12:37:22.000 31010-31010/com.ihuntto.hellogson D/MainActivity: {"shapes":[{"radius":10.0,"type":"circle","x":0.0,"y":0.0},{"height":10.0,"type":"rectangle","width":20.0,"x":0.0,"y":0.0}]}
2023-12-17 12:37:22.006 31010-31010/com.ihuntto.hellogson D/Circle: draw Circle{x=0.0, y=0.0, radius=10.0}
2023-12-17 12:37:22.006 31010-31010/com.ihuntto.hellogson D/Rectangle: draw Rectangle{x=0.0, y=0.0, width=20.0, height=10.0}

如果我定义了一个ShapeCompounds

public class ShapeCompounds implements IShape {
    private static final String TAG = ShapeCompounds.class.getSimpleName();
    String type = "shape-compounds";

    List<IShape> shapes = new ArrayList<>();

    @Override
    public void draw() {
        Log.d(TAG, "draw " + toString());
    }

    @NonNull
    @Override
    public String toString() {
        return "ShapeCompounds{" +
                "type='" + type + '\'' +
                ", shapes=" + shapes +
                '}';
    }
}

并且在IShapeDeserializer进行注册:

public class IShapeDeserializer implements JsonDeserializer<IShape> {
    ...
    public IShapeDeserializer() {
        ...
        mTypeMap.put("shape-compounds", ShapeCompounds.class);
    }
    ...
}

那么下面这段代码还能运行正常吗?

IShape circle = new Circle(0, 0, 10);
IShape rectangle = new Rectangle(0, 0, 20, 10);
ShapeCompounds compounds = new ShapeCompounds();
compounds.shapes.add(circle);
compounds.shapes.add(rectangle);
ShapeHolder holder = new ShapeHolder();
holder.shapes.add(circle);
holder.shapes.add(rectangle);
holder.shapes.add(compounds);

Gson gson = new GsonBuilder().registerTypeAdapter(IShape.class, new IShapeDeserializer()).create();

String json = gson.toJson(holder);
Log.d(TAG, json);

ShapeHolder shapeHolder = gson.fromJson(json, ShapeHolder.class);
for (IShape shape : shapeHolder.shapes) {
    shape.draw();
}

很不辛,上述代码可以序列化成功,但反序列化失败,如果在IShapeDeserializerdeserialize方法出加上打印可以知道原因是无法反序列化ShapeCompounds,因为序列化ShapeCompoundsGson为普通Gson,并没有注册TypeAdapter,因此同样需要为IShapeDeserializer注册TypeAdapter

public class IShapeDeserializer implements JsonDeserializer<IShape> {
    private static Gson sInstance;
    private final HashMap<String, Class<? extends IShape>> mTypeMap = new HashMap<>();

    private IShapeDeserializer() {
        mTypeMap.put("circle", Circle.class);
        mTypeMap.put("rectangle", Rectangle.class);
        mTypeMap.put("shape-compounds", ShapeCompounds.class);
    }

    @Override
    public IShape deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        Class<? extends IShape> clazz = mTypeMap.get(json.getAsJsonObject().get("type").getAsString());
        return sInstance.fromJson(json, clazz);
    }


    public static Gson crate() {
        if (sInstance == null) {
            sInstance = new GsonBuilder().registerTypeAdapter(IShape.class, new IShapeDeserializer()).create();
        }
        return sInstance;
    }
}

将测试代码中的gson对象创建方法替换为:

Gson gson = IShapeDeserializer.crate();

运行测试代码就可以得到正常打印了:

2023-12-17 12:54:41.038 31506-31506/com.ihuntto.hellogson D/MainActivity: {"shapes":[{"radius":10.0,"type":"circle","x":0.0,"y":0.0},{"height":10.0,"type":"rectangle","width":20.0,"x":0.0,"y":0.0},{"shapes":[{"radius":10.0,"type":"circle","x":0.0,"y":0.0},{"height":10.0,"type":"rectangle","width":20.0,"x":0.0,"y":0.0}],"type":"shape-compounds"}]}
2023-12-17 12:54:41.044 31506-31506/com.ihuntto.hellogson D/Circle: draw Circle{x=0.0, y=0.0, radius=10.0}
2023-12-17 12:54:41.044 31506-31506/com.ihuntto.hellogson D/Rectangle: draw Rectangle{x=0.0, y=0.0, width=20.0, height=10.0}
2023-12-17 12:54:41.045 31506-31506/com.ihuntto.hellogson D/ShapeCompounds: draw ShapeCompounds{type='shape-compounds', shapes=[Circle{x=0.0, y=0.0, radius=10.0}, Rectangle{x=0.0, y=0.0, width=20.0, height=10.0}]}

这里仅总结我在使用Gson过程中遇到的问题及解决的方法,后续遇到新的问题再进行更新。

参考

  1. Gson
  2. Gson工具的使用、序列化/反序列化集合、对象、遇到的问题
  3. Gson反序列化 子类、接口实现类

标签:0.0,new,class,使用,Gson,Android,序列化,public
From: https://www.cnblogs.com/huntto/p/17908970.html

相关文章

  • android 获取模数
    背景:政策要求App要备案。1.根据阿里云文档[获取App特征](https://help.aliyun.com/zh/icp-filing/fill-in-app-feature-information),我们需要使用JadxGUI工具,于是我们搜索JadxGUI如何安装使用,接下来就开始安装。2.下载JadxGUI源码,[原文](https://www.jianshu.com/p/3cc4e861b3db)......
  • 使用 Prometheus 监控 Memcached 最佳实践
    监控Memcached监控Memcached的重要性无法忽视。通过监控Memcached,我们可以及时了解到系统中的缓存命中率、缓存使用量和缓存访问频率等关键指标,从而更好地优化系统性能。同时,监控Memcached还可以帮助我们发现潜在的性能问题和内存泄漏,及时采取措施进行修复,保障系统的稳定运行。在......
  • ARM64技术 —— 系统调用指令SVC、HVC和SMC的使用规则
    来源:learn_the_architecture_-_aarch64_exception_model_102412_0103_01_en.pdf即:EL0不能调用SMC或者HVC这个从HVC和SMC指令的伪码中可以看到:HVCSMC......
  • 02.环境安装与使用
    目前mobile自动化解决方案iOSAndroidCalabash-iOScalabash-AndroidFrankMonkeyTalkUIAutomationRobotiumiOS-driverUIAutomatorKeepItFunctionalSelendroidMacacaMacacaAppiumAppium 自动化工具选择工具名称被测系统脚本语言是否支......
  • Android深入学习之Activity与Fragment之间回调函数的调用顺序
    本文使用的例子是用WelcomeActivity托管WelcomeFragment。先来看Log。1.WelcomeActivityWelcomeActivitycreated!2.WelcomeActivityonCreate2.1.WelcomeFragmentWelcomeFragmentcreated!2.2.FragmentManagerCommit:BackStackEntry{cc......
  • 使用Docker自定义配置部署RustDesk Server
    “RustDesk是一款可以平替TeamViewer的开源软件,旨在提供安全便捷的自建方案。”这是RustDesk官网对自己的描述。作为一款使用Rust语言开发的开源软件,在为数不多的Rust开发者和数量庞大的Rust学习者中还是有相当的知名度的,并且商业化的RustDeskPro也是如火如荼。开始docker......
  • Spring Boot Actuator 使用和常用配置
    转载请注明出处:SpringBootActuator是SpringBoot提供的一个非常强大的工具,它可以帮助我们监控和管理我们的SpringBoot应用。Actuator提供了一系列的端点,我们可以通过这些端点来查看我们的应用的运行状态,例如健康状态、指标、信息、数据库连接、Redis连接等等1.配置Spring......
  • 使用LM Studio在本地运行LLM完整教程
    GPT-4被普遍认为是最好的生成式AI聊天机器人,但开源模型一直在变得越来越好,并且通过微调在某些特定领域是可以超过GPT4的。在开源类别中出于以下的原因,你可能会考虑过在本地计算机上本地运行LLM:脱机:不需要互联网连接。模型访问:在本地运行模型,可以尝试开源模型(Llama2、Vicun......
  • 使用JS和SignalR完成双向通信
    写在前面:看官网介绍,听开发者朋友的感受,SignalR对websocket等多个长连接协议进行了封装,提供了多种方法,能够适应很多场合和复杂情况。单纯论简单的web长连接,它确实没有js/nodejs版的websocket直观、简单。微软的风格,一来就高大上,不知道又会劝退多少人,会不会把自己玩死。我帮你简单......
  • SpringBoot集成Swagger的使用
    一、前言Swagger是一个规范和完整的框架,用于生成、描述、调用和可视化RESTful风格的Web服务。目标是使客户端和文件系统作为服务器以同样的速度来更新文件的方法,参数和模型紧密集成到服务器。Swagger能够在线自动生成RESTFul接口的文档,同时具备测试接口的功能。简单点来讲就是说......