1. 问题场景
其实,网络传输中JSON数据的构建已经有非常成熟、方便的方案,但是实际工作中难免会遇到一些不规范的特殊情况,例如:
- 相似的数据同一字段在不同接口具有不同意义,不同的值的范围;
- 同一业务实体在不同接口交互中字段的数量不同,比如,一些需要A字段,而一些没有。
这些问题使得在利用框架和库的功能来构造JSON时显得不是很灵活。而本文就向大家介绍一个用于灵活构建JSON的工具类–JsonBuilder。(完整代码见最后)
2. 常见的JSON构建方式
首先,我们先来看一下通常可能出现的JSON数据构建方式,并分析其优缺点。
2.1 直接使用JSON三方库的API
这种方式指的是直接使用Gson、FastJson等流行的JSON库提供的API,例如下方使用gson的代码:
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("id", 1);
jsonObject.addProperty("name", "test name");
String json = jsonObject.toString();
优点:灵活(可以直接构建出所有JSON),对于临时数据不会产生额外的类
缺点:不方便,不易读,不容易维护(尤其是字段多、结构复杂的时候)
2.2 类对象映射
这种方式指的是把一个类的对象直接转换成JSON字符串的方式,也是最常用的方式,可以集成到框架中,在项目规范的情况下,应该是最完美的方式。例如下方使用gson的代码:
Gson gson = new Gson();
User user = new User();
user.setId(1);
user.setName("test name");
String json = gson.toJson(user);
优点:方便(比较适合集成到框架中),易读,维护成本低(项目规范的情况下)
缺点:不是特别灵活(构造临时数据会导致一堆临时的类)
2.3 直接编写JSON字符串
基本没人用,不多解释,也不需解释
String json = "{\"id\":1,\"name\":\"test name\"}";
优点:无(即使有我也不说)
缺点:不容易维护,容易出问题,等等等等。
3. 使用示例
- 构建JSON对象
String json = JsonBuilder.forObject()
        .with("id", 1)
        .with("name", "test name")
        .build();
- 构建嵌套的JSON对象
//对象嵌套对象
String json = JsonBuilder.forObject()
        .with("id", 1)
        .with("name", "test name")
        .with("role", JsonBuilder.forObject()
                .with("id", 11)
                .with("name", "student")
                .toJsonObject())
        .build();//{"id":1,"name":"test name","role":{"id":11,"name":"student"}}
- 构建JSON数组
String json = JsonBuilder.forArray()
        .add(1)
        .add(2)
        .build();//[1,2]
- 构建包含数组的对象
json = JsonBuilder.forObject()
        .with("id", 1)
        .with("array", JsonBuilder.forArray()
                .add(1)
                .add(2)
                .toJsonArray())
        .build();//{"id":1,"array":[1,2]}
- 构建包含数组的数组
json = JsonBuilder.forArray()
        .add(1)
        .add(JsonBuilder.forArray()
                .add("s1")
                .add("s2")
                .toJsonArray())
        .build();//[1,["s1","s2"]]
- 构建包含对象的数组
json = JsonBuilder.forArray()
        .add(JsonBuilder.forObject()
                .with("id", 1)
                .with("name", "xiao ming")
                .toJsonObject())
        .add(JsonBuilder.forObject()
                .with("id", 2)
                .with("name", "ding ding")
                .toJsonObject())
        .build();//[{"id":1,"name":"xiao ming"},{"id":2,"name":"ding ding"}]
- 基于已有JSON来构建对象
String json = "{\"id\":1,\"name\":\"test name\"}";
json = JsonBuilder.fromObject(json)
        .with("sex", "male")
        .build();//{"id":1,"name":"test name","sex":"male"}
- 基于已有JSON来构建数组
String json = "[1,2]";
json = JsonBuilder.fromArray(json)
        .add(3)
        .add(4)
        .build();//[1,2,3,4]
4. 功能介绍
这个工具类是基于建造者模式的思想来打造的,利用方法的链式调用来达到流畅编写、表达明确的目的。
4.1 构建的开始和结束
JsonBuilder提供了以下静态方法,用于json构建的开始:
- forArray()和- forArray(int capacity)用于构建数组;
- forObject()用于构建对象;
- fromObject(String json)用于从已有的json来构建对象;
- fromArray(String json)用于从已有的json来构建数组。
而完成json构建则依赖于对抽象方法build()的调用。
4.2 with、add 和 set 方法
with方法用于向对象中添加字段属性,包含以下重载形式:
- with(String key, String value):添加字符串字段
- with(String key, Number value):添加数字字段
- with(String key, boolean value):添加布尔字段
- with(String key, Character character):添加字符字段
- with(String key, JsonElement element):添加其他JSON元素(具体见下方对- toJsonObject和- toJsonArray方法的说明)
add方法用于向数组中添加元素,包含以下重载形式:
- add(boolean value):添加布尔
- add(String value):添加字符串
- add(Character character):添加字符
- add(Number number):添加数字
- add(JsonElement json):添加其他JSON元素(具体见下方对- toJsonObject和- toJsonArray方法的说明)
set方法用于设置数组中制定索引的元素,包含以下重载形式:
- set(int index, boolean value):设置index处的元素为对应布尔值value
- set(int index, String value):设置index处的元素为对应字符串value
- set(int index, Character character):设置index处的元素为对应字符value
- set(int index, Number number):设置index处的元素为对应数字value
- set(int index, JsonElement element)::设置index处的元素为对应JSON元素value
4.3 toJsonObject 和 toJsonArray 方法
可以用这两个方法返回的值来调用with(String key, JsonElement element)或add(JsonElement json)进而实现JSON数据的嵌套。
例如,如果我们像实现下方这样的JSON:
{
  "id": 1,
  "name": "test name",
  "role": {
    "id": 11,
    "name": "student"
  }
}
使用JSON的API来构建就是:
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("id", 1);
jsonObject.addProperty("name", "test name");
JsonObject roleJson = new JsonObject();
roleJson.addProperty("id", 11);
roleJson.addProperty("name", "student");
jsonObject.add("role", roleJson);
String json = jsonObject.toString();
如果用对象映射的方式:
Gson gson = new Gson();
User user = new User();
user.setId(1);
user.setName("test name");
Role role = new Role();
role.setId(11);
role.setName("student");
user.setRole(role);
String json = gson.toJson(user);
这里要注意到jsonObject.add("role", roleJson);和user.setRole(role);才能察觉是个嵌套结构,换成JsonBuilder后:
String json = JsonBuilder.forObject()
        .with("id", 1)
        .with("name", "test name")
        .with("role", JsonBuilder.forObject()
                .with("id", 11)
                .with("name", "student")
                .toJsonObject())
        .build();
JSON数据的结构一目了然。(嵌套的数据就用嵌套的代码)
5. 完整代码
import androidx.annotation.NonNull;
import com.google.gson.*;
public abstract class JsonBuilder {
    @NonNull
    @Override
    public String toString() {
        return build();
    }
    public abstract String build();
    public static ObjectBuilder forObject() {
        return new ObjectBuilder();
    }
    public static ArrayBuilder forArray() {
        return new ArrayBuilder();
    }
    public static ArrayBuilder forArray(int capacity) {
        return new ArrayBuilder(capacity);
    }
    public static ObjectBuilder fromObject(String json) {
        return new ObjectBuilder(json);
    }
    public static ArrayBuilder fromArray(String json) {
        return new ArrayBuilder(json);
    }
    public static class ObjectBuilder extends JsonBuilder {
        private final JsonObject object;
        ObjectBuilder() {
            this.object = new JsonObject();
        }
        ObjectBuilder(String json) {
            object = JsonParser.parseString(json).getAsJsonObject();
        }
        public ObjectBuilder with(String key, JsonElement element) {
            object.add(key, element);
            return this;
        }
        public ObjectBuilder with(String key, String value) {
            object.addProperty(key, value);
            return this;
        }
        public ObjectBuilder with(String key, Number value) {
            object.addProperty(key, value);
            return this;
        }
        public ObjectBuilder with(String key, boolean value) {
            object.addProperty(key, value);
            return this;
        }
        public ObjectBuilder with(String key, Character character) {
            object.addProperty(key, character);
            return this;
        }
        @Override
        public String build() {
            return object.toString();
        }
        public JsonObject toJsonObject() {
            return object;
        }
    }
    public static class ArrayBuilder extends JsonBuilder {
        private final JsonArray array;
        ArrayBuilder(String json) {
            array = JsonParser.parseString(json).getAsJsonArray();
        }
        ArrayBuilder(int capacity) {
            this.array = new JsonArray(capacity);
        }
        ArrayBuilder() {
            this.array = new JsonArray();
        }
        public ArrayBuilder add(boolean value) {
            array.add(value);
            return this;
        }
        public ArrayBuilder add(String value) {
            array.add(value);
            return this;
        }
        public ArrayBuilder add(Character character) {
            array.add(character);
            return this;
        }
        public ArrayBuilder add(Number number) {
            array.add(number);
            return this;
        }
        public ArrayBuilder add(JsonElement json) {
            array.add(json);
            return this;
        }
        public ArrayBuilder set(int index, boolean value) {
            array.set(index, new JsonPrimitive(value));
            return this;
        }
        public ArrayBuilder set(int index, String value) {
            array.set(index, new JsonPrimitive(value));
            return this;
        }
        public ArrayBuilder set(int index, Character character) {
            array.set(index, new JsonPrimitive(character));
            return this;
        }
        public ArrayBuilder set(int index, Number number) {
            array.set(index, new JsonPrimitive(number));
            return this;
        }
        public ArrayBuilder set(int index, JsonElement element) {
            array.set(index, element);
            return this;
        }
        @Override
        public String build() {
            return array.toString();
        }
        public JsonArray toJsonArray() {
            return array;
        }
    }
}
附注:这个类的主要功能基于gson来实现,如果要改为其他的json库,只需要把JsonElement、JsonArray和JsonObject换成对应的类,修改toJsonArray和toJsonObject方法的返回值即可。










