GSON使用的学习笔记,进阶篇(一)

本文探讨了在处理JSON字符串时,如何有效利用GSON库进行解析与简化代码,包括自定义解析过程、使用注解及简化JavaBean定义等方法,以提高开发效率并减少代码冗余。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

自定义解析过程

        这里把问题简化一下,比如获得到的json字符串是 [{"name":"Jackie","age":30,"contact":{"email":"email","phoneno":"phoneno"}},{"name":"Jackie Boy","age":1,"contact":{"email":"email","phoneno":"phoneno"}}]。但其中的age字段对我来说没有意义,因而不想在Bean里专门定义一个字段用来承载。

使用JsonReader

        依靠gson提供的类JsonReader,用如下一段样例程序来说明如何解决当前的难题。

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import lombok.Data;

import com.google.gson.Gson;
import com.google.gson.stream.JsonReader;

public class JsonTest {
    public static void main(final String[] args) {
        final Gson gson = new Gson();
        final Person jack1 = new Person();
        jack1.setAge(30);
        jack1.setName("Jackie");

        jack1.putValue("email", "email");
        jack1.putValue("phoneno", "phoneno");

        final Person jack2 = new Person();
        jack2.setAge(1);
        jack2.setName("Jackie Boy");
        jack2.putValue("email", "email");
        jack2.putValue("phoneno", "phoneno");

        final String json = gson.toJson(Arrays.asList(jack1, jack2));
        System.out.println(json);

        List<Person> jacks = null;
        // 自定义解码处理过程,跳过对字段age的处理
        JsonReader reader = null;
        InputStream in = null;
        try {
            in = new ByteArrayInputStream(json.getBytes("UTF-8"));
            reader = new JsonReader(new InputStreamReader(in, "UTF-8"));

            jacks = decode(reader);

            for (final Person person : jacks) {
                System.out.println("jack = " + person);
            }
        }
        catch (final Exception e) {
            e.printStackTrace();
        }
        finally {
            if (in != null) {
                try {
                    in.close();
                }
                catch (final IOException e) {
                    e.printStackTrace();
                }
            }
            if (reader != null) {
                try {
                    reader.close();
                }
                catch (final IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    // 如下方法是重点,解析操作的处理过程
    private static List<Person> decode(JsonReader reader) throws IOException {
        List<Person> jacks;
        jacks = new ArrayList<Person>();
        reader.beginArray();// 通知gson框架,这里开始解析的是数组类型
        while (reader.hasNext()) {
            final Person p = new Person();
            jacks.add(p);
            reader.beginObject();// 通知gson框架,这里开始解析的是对象
            while (reader.hasNext()) {
                final String name = reader.nextName();// 提到名、值对中的名
                if (name.equals("name")) {
                    final String value = reader.nextString();
                    p.setName(value);
                }
                else if (name.equals("contact")) {
                    reader.beginObject();// 通知gson框架,这里开始解析的是对象
                    while (reader.hasNext()) {
                        final String key = reader.nextName();
                        final String value = reader.nextString();
                        p.putValue(key, value);
                    }
                    reader.endObject();// 通知gson框架,对对象的解析完成
                }
                else {
                    reader.skipValue();// 跳过不必要的字段,根据之前的设定,这里只能是age
                }
            }
            reader.endObject();// 通知gson框架,对对象的解析完成

        }
        reader.endArray();// 通知gson框架,数组对象的解析完成
        return jacks;
    }
}

@Data
class Person {
    private String name;
    private int age;
    private Map<String, Object> contact;

    public Person() {
        contact = new HashMap<String, Object>();
    }

    public void putValue(final String key, final Object value) {
        contact.put(key, value);
    }
}

        上述代码执行后的输出如下。从输出可以看出,age字段没有赋值,说明上述代码功能正确。

[{"name":"Jackie","age":30,"contact":{"email":"email","phoneno":"phoneno"}},{"name":"Jackie Boy","age":1,"contact":{"email":"email","phoneno":"phoneno"}}]
jack = Person(name=Jackie, age=0, contact={email=email, phoneno=phoneno})
jack = Person(name=Jackie Boy, age=0, contact={email=email, phoneno=phoneno})

        问题虽然得到了解决,但代价有点高,代码有点多。上述代码用来练练手、学习一下gson的基本原理还行,但如果在项目里也这么应用,一方面太复杂,另一方面gson的能力也没发挥出来。假如缩小问题范围,增加一些约束,比如在定义Java Bean时成员名称和待解析的json串中的字段名相同,并且各字段的值在解析时不需要做复杂的处理,那么依赖强大的gson,上述代码可以做一些简化。

import java.io.ByteArrayInputStream;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import lombok.Data;

import com.google.gson.Gson;
import com.google.gson.stream.JsonReader;

public class JsonTest {
    public static void main(final String[] args) {
        final Gson gson = new Gson();
        final Person jack1 = new Person();
        jack1.setAge(30);
        jack1.setName("Jackie");

        jack1.putValue("email", "email");
        jack1.putValue("phoneno", "phoneno");

        final Person jack2 = new Person();
        jack2.setAge(1);
        jack2.setName("Jackie Boy");
        jack2.putValue("email", "email");
        jack2.putValue("phoneno", "phoneno");

        final String json = gson.toJson(new Person[] { jack1, jack2 });
        System.out.println(json);

        final Person2[] persons = gson.fromJson(json, Person2[].class);
        for (final Person2 p : persons) {
            System.out.println(p);
        }
    }
}

@Data
class Person {
    private String name;
    private int age;
    private Map<String, Object> contact;

    public Person() {
        contact = new HashMap<String, Object>();
    }

    public void putValue(final String key, final Object value) {
        contact.put(key, value);
    }
}
// 删除age成员
@Data
class Person2 {
    private String name;
    private Map<String, Object> contact;

    public Person2() {
        contact = new HashMap<String, Object>();
    }

    public void putValue(final String key, final Object value) {
        contact.put(key, value);
    }
}
        依赖gson的能力,可以定义一个相同的Java Bean,同时忽略掉不必要的字段age,使用常规的方法即可以完成解析操作。不爽之处在于需要定义一个新的Java Bean。

使用注解

        gson的注解Expose是一个有趣的特性,可以在上前述简化方案的基础上少定义一个Java Bean类,但也引入了新的约束,如下是完整的样例。

import java.util.HashMap;
import java.util.Map;

import lombok.Data;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.annotations.Expose;

public class JsonTest {
    public static void main(final String[] args) {
        // final Gson gson = new Gson();
        final Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();// 表示通过注解来控制json的解析和生成

        final Person jack1 = new Person();
        jack1.setAge(30);
        jack1.setName("Jackie");

        jack1.putValue("email", "email");
        jack1.putValue("phoneno", "phoneno");

        final Person jack2 = new Person();
        jack2.setAge(1);
        jack2.setName("Jackie Boy");
        jack2.putValue("email", "email");
        jack2.putValue("phoneno", "phoneno");

        final String json = gson.toJson(new Person[] { jack1, jack2 });
        System.out.println(json);

        final Person[] persons = gson.fromJson(json, Person[].class);
        for (final Person p : persons) {
            System.out.println(p);
        }
    }
}

@Data
class Person {
    @Expose
    private String name;
    @Expose(serialize = true, deserialize = false)// 表示在生成json字符串时输出age成员,但由字符串解析得到对象时,不去获取age字段的值
    private int age;// 对于没有使用@Expose标记的字段,则不会在json字符串中出现,同样也不会被解析
    @Expose
    private Map<String, Object> contact;

    public Person() {
        contact = new HashMap<String, Object>();
    }

    public void putValue(final String key, final Object value) {
        contact.put(key, value);
    }
}
        

===================

        我本人接触json比较晚,一直以来做的都是C/S架构的产品,如果这次不是项目需要,肯定不会闲来无事想起来研究用GSON来解析json字符串。从我本人的经历看,json可能的使用场景有如下几种:

1、向页面传递数据,需要把数据对象编码成json格式,方便页面使用。页面在收到json格式的数据之后,只需要一行代码即可以转换为javascript对象,然后即可按照格式提取数据,非常方便。如果向页面传递的数据结构不是很复杂,那么不见得要使用gson,手写代码一样可以胜任,缺点是有一定的侵入性,json格式变更或者数据对象有变更时,编码相关的方法肯定需要修改。另外手写编码器的过程比较枯燥,创造性的工作反而成了苦力,这点令人不爽,重复而简单的工作应当交给机器来做。使用gson来进行编码的话,一般只需要修改Java Bean的定义,其它烦恼事都交给gson做,因而基本没有什么工作量,相比较而言省事、方便一些。

2、页面向后台传递数据,页面把数据编码成了json格式,后台完成相关的数据处理,这时需要把数据从json串里提取出来。理论上讲,页面向服务端传递的数据一般来源于用户的键盘输入,规模不会太大,格式也比较简单,所以手写解码代码基本可以解决相当一部分问题。

3、部件之间通信时使用json格式做为消息编码方式,这样收和发消息的部件都需要实现json编码、解码的操作。从我个人的经验看,部件之间的通信格式是程序员或者设计师设计的,从减少自身工作量的角度考虑,数据的格式都不会太复杂,但部件之间的通信接口比较多,使用gson可以极大的提升开发效率,快速完成特性的开发。

===================

        项目里遇到的场景比较奇葩,待解析的json格式的字符串中还存在一些不必要的字段,但是提供数据的部件为了实现简单,以及所谓的数据完整性,并没有把我当前不关心的数据过滤掉;而我需要的数据格式和对方提供的格式存在较大的差异,这样就不能通过直接对照结构定义Java Bean来直接完成解析操作。现实问题摆在眼前,必须对json解析的过程进行某种控制,使其生成我需要的数据,同时忽略不关心的字段。但是gson很强大,控制解析过程的想法完全是多余的,只要在Java Bean的定义中忽略不必要的字段,然后把类型传递给gson对象来解析输入的字符串,直接就可以得到对应的Java Bean,不需要做特别的处理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小南家的青蛙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值