Java处理JSON数据通常的做法就是通过第三方库将其转换为一个Java类的对象,但是这样会导致产生很多跟产品业务无关的临时类。在JavaScript中,则可以直接提取,例如obj.user.name
,非常方便。但是在Java中,如果不转换为类的对象,就得小心翼翼地编写像下面这样的代码:
try {
JsonElement element = JsonParser.parseString(jsonStr);
if (element.isJsonObject()) {
JsonObject userJson = element.getAsJsonObject().getAsJsonObject("user");
if (userJson != null) {
JsonPrimitive nameJson = userJson.getAsJsonPrimitive("name");
if (nameJson != null && nameJson.isString()) {
String name = nameJson.getAsString();
}
}
}
} catch (JsonParseException e) {
e.printStackTrace();
}
这样的方式非常繁琐,而且里面充满防御性的代码,显得罗嗦,关键信息其实只有jsonStr.user.name
这样。而本文要介绍的就是不将JSON数据转换为某个 Java Bean 对象来提取JSON数据内容的一个工具类–JsonExtractor
。
这个类比较简单,但是方法不少,因此我将这个类里的方法分为两类来讲
如名,用于构建JsonExtractor
的方法,这里不推荐直接调用构造方法,而是以下几个方法来构建
Builder from(String json)
:这个方法是整个过程的开始,传入参数为需要解析的JSON字符串,返回的对象是用于构建JsonExtractor
的建造者。JsonExtractor forJsonArray()
:这个方法指定要处理的JSON数组是数组类型,返回对应的处理JSON数组的提取器。JsonExtractor forNull()
:这个方法指定要处理的JSON数据是null,基本不用。JsonExtractor forPrimitive()
:这个方法指定要处理的JSON数据是原始类型(例如,int、String等等),返回处理原始类型的提取器。JsonExtractor forJsonObject()
:这个方法指定要处理的JSON数据是JSON对象,返回处理JSON对象的提取器,比较常用。这些方法的参数根据JsonExtractor
的泛型K决定,如果当前对象为数组,则K为Integer
,表示数组的索引;如果是对象,则K为String
,表示属性对应的键值;如果是原始类型或null,由于没有下一级属性或内容,因此为Void
。
JsonArray getArray(K key)
:提取对应键值的属性为数组。Optional optArray(K key)
:getArray
的Optional版本。int getInt(K key)
:提取对应键值的属性为int
,没有则返回0。Optional optInt(K key)
:getInt
的Optional版本。boolean getBool(K key)
:提取对应键值的属性为boolean
,没有则返回false。Optional optBool(K key)
:getBool
的Optional版本。long getLong(K key)
:提取对应键值的属性为long
,没有则返回0。Optional optLong(K key)
:getLong
的Optional版本。String getString(K key)
:提取对应键值的属性为字符串,没有则返回空字符串。Optional optString(K key)
:getString
的Optional版本。JsonElement get(K key)
和 T get(K key, Class typeClass)
:提取指定键值的属性为JsonElement或指定Java类型。Optional opt(K key)
和 Optional opt(K key, Class typeClass)
:get
的Optional版本。Builder into(K key)
:对指定键值的属性进行下一步提取,用于像data.user.name
这样的多级提取。void forEach(BiConsumer entryConsumer)
:对JSON数据的内容进行遍历。例如,我们要提取的json如下:
{
"id": 1,
"name": "Li Lei",
"pet": {
"name": "huahua",
"type": "dog",
"id": 11
},
"live": true,
"age": 20,
"friends": [
"James",
"Andy",
"Tom"
]
}
对应的 Java Bean 如下:
class Pet {
private String name;
private String type;
private long id;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
}
class User {
private long id;
private String name;
private Pet pet;
private boolean live;
private int age;
private String[] friends;
public String[] getFriends() {
return friends;
}
public void setFriends(String[] friends) {
this.friends = friends;
}
public boolean isLive() {
return live;
}
public void setLive(boolean live) {
this.live = live;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Pet getPet() {
return pet;
}
public void setPet(Pet pet) {
this.pet = pet;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
那么提取代码就是:
String json = "{\"id\":1,\"name\":\"Li Lei\",\"pet\":{\"name\":\"huahua\",\"type\":\"dog\",\"id\":11},\"live\":true,\"age\":20,\"friends\":[\"James\",\"Andy\",\"Tom\"]}";
JsonExtractor extractor = JsonExtractor.from(json).forJsonObject();
//直接获取属性
long id = extractor.getLong("id");
//按Optional风格获取基本类型的属性
extractor.optString("name").ifPresent((String name) -> {
System.out.println("name:" + name);
});
extractor.optString("name").ifPresent(System.out::println);
//按Optional风格获取属性并手动转换为指定类
extractor.opt("pet").ifPresent((JsonElement element) -> {
System.out.println("pet json:" + element);
Pet pet = new Gson().fromJson(element, Pet.class);
System.out.println("pet bean 1:" + pet.getName());
});
//按Optional风格获取属性自动转换为指定类
extractor.opt("pet", Pet.class).ifPresent((Pet pet) -> {
System.out.println("pet bean 2:" + pet.getName());
});
//直接将属性提取为指定类型
Pet pet = extractor.get("pet", Pet.class);
//提取多级字段,类似js的 user.pet.name 这样的风格
extractor.into("pet").forJsonObject()
.optString("name").ifPresent((String name) -> {
System.out.println("pet.name:" + name);
});
//提取并遍历数组
extractor.into("friends").forJsonArray()
.forEach((Integer index, JsonElement element) -> {
System.out.println(index + ":" + element.getAsString());
});
import com.google.gson.*;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.BiConsumer;
public abstract class JsonExtractor {
private static final Gson gson = new Gson();
protected V jsonElement;
JsonExtractor(V jsonElement) {
this.jsonElement = jsonElement;
}
public V getJsonElement() {
return jsonElement;
}
public static class Builder {
private JsonElement element;
protected Builder() {
}
public Builder(JsonElement element) {
this.element = element;
}
public JsonExtractor forNull() {
try {
return new NullExtractor(element.getAsJsonNull());
} catch (RuntimeException e) {
return new ErrorExtractor<>(e);
}
}
public JsonExtractor forPrimitive() {
try {
return new PrimitiveExtractor(element.getAsJsonPrimitive());
} catch (RuntimeException e) {
return new ErrorExtractor<>(e);
}
}
public JsonExtractor forJsonObject() {
try {
return new ObjectExtractor(element.getAsJsonObject());
} catch (RuntimeException e) {
return new ErrorExtractor<>(e);
}
}
public JsonExtractor forJsonArray() {
try {
return new ArrayExtractor(element.getAsJsonArray());
} catch (RuntimeException e) {
return new ErrorExtractor<>(e);
}
}
}
private static class ErrorBuilder extends Builder {
private final Exception exception;
public ErrorBuilder(Exception exception) {
this.exception = exception;
}
@Override
public JsonExtractor forJsonArray() {
return new ErrorExtractor<>(exception);
}
@Override
public JsonExtractor forNull() {
return new ErrorExtractor<>(exception);
}
@Override
public JsonExtractor forPrimitive() {
return new ErrorExtractor<>(exception);
}
@Override
public JsonExtractor forJsonObject() {
return new ErrorExtractor<>(exception);
}
}
public static Builder from(String json) {
try {
return new Builder(JsonParser.parseString(json));
} catch (Exception e) {
return new ErrorBuilder(e);
}
}
public Builder into(K key) {
return new ErrorBuilder(new RuntimeException("not implements!"));
}
public Optional opt(K key) {
return Optional.ofNullable(get(key));
}
public Optional opt(K key, Class typeClass) {
return Optional.empty();
}
public JsonElement get(K key) {
return null;
}
public T get(K key, Class typeClass) {
return opt(key, typeClass).orElse(null);
}
public JsonArray getArray(K key) {
return null;
}
public Optional optArray(K key) {
return Optional.ofNullable(getArray(key));
}
public int getInt(K key) {
return optInt(key).orElse(0);
}
public Optional optInt(K key) {
return Optional.empty();
}
public double getDouble(K key) {
return optDouble(key).orElse(0d);
}
public Optional optDouble(K key) {
return Optional.empty();
}
public boolean getBool(K key) {
return optBool(key).orElse(false);
}
public Optional optBool(K key) {
return Optional.empty();
}
public long getLong(K key) {
return optLong(key).orElse(0L);
}
public Optional optLong(K key) {
return Optional.empty();
}
public String getString(K key) {
return optString(key).orElse("");
}
public Optional optString(K key) {
return Optional.empty();
}
public void forEach(BiConsumer entryConsumer) {
}
private static class ObjectExtractor extends JsonExtractor {
private ObjectExtractor(JsonObject jsonObject) {
super(jsonObject);
}
@Override
public Builder into(String key) {
JsonElement element = this.jsonElement.get(key);
return element == null ? new Builder(JsonNull.INSTANCE) : new Builder(element);
}
@Override
public JsonElement get(String key) {
return this.jsonElement.getAsJsonObject(key);
}
@Override
public Optional optArray(String key) {
return Optional.ofNullable(this.jsonElement.getAsJsonArray(key));
}
@Override
public Optional optInt(String key) {
return Optional.ofNullable(this.jsonElement.getAsJsonPrimitive(key)).map(JsonPrimitive::getAsInt);
}
@Override
public Optional optDouble(String key) {
return Optional.ofNullable(jsonElement.getAsJsonPrimitive(key)).map(JsonPrimitive::getAsDouble);
}
@Override
public Optional optBool(String key) {
return Optional.ofNullable(jsonElement.getAsJsonPrimitive(key)).map(JsonPrimitive::getAsBoolean);
}
@Override
public Optional optLong(String key) {
return Optional.ofNullable(jsonElement.getAsJsonPrimitive(key)).map(JsonPrimitive::getAsLong);
}
@Override
public Optional optString(String key) {
return Optional.ofNullable(jsonElement.getAsJsonPrimitive(key)).map(JsonPrimitive::getAsString);
}
@Override
public Optional opt(String key, Class typeClass) {
try {
return Optional.ofNullable(jsonElement.get(key)).map(e -> gson.fromJson(e, typeClass));
} catch (JsonSyntaxException e) {
return Optional.empty();
}
}
@Override
public void forEach(BiConsumer entryConsumer) {
Set> entrySet = jsonElement.entrySet();
for (Map.Entry entry : entrySet) {
entryConsumer.accept(entry.getKey(), entry.getValue());
}
}
}
private static class ArrayExtractor extends JsonExtractor {
private ArrayExtractor(JsonArray array) {
super(array);
}
@Override
public Builder into(Integer index) {
try {
return new Builder(getJsonElement().get(index));
} catch (IndexOutOfBoundsException e) {
return new ErrorBuilder(e);
}
}
@Override
public JsonElement get(Integer index) {
try {
return getJsonElement().get(index);
} catch (IndexOutOfBoundsException e) {
return null;
}
}
@Override
public Optional optArray(Integer index) {
return Optional.ofNullable(getJsonElement().get(index)).map(JsonElement::getAsJsonArray);
}
@Override
public Optional optInt(Integer index) {
try {
return Optional.ofNullable(getJsonElement().get(index)).map(JsonElement::getAsInt);
} catch (IndexOutOfBoundsException e) {
return Optional.empty();
}
}
@Override
public Optional optDouble(Integer index) {
try {
return Optional.ofNullable(getJsonElement().get(index)).map(JsonElement::getAsDouble);
} catch (IndexOutOfBoundsException e) {
return Optional.empty();
}
}
@Override
public Optional optBool(Integer index) {
try {
return Optional.ofNullable(getJsonElement().get(index)).map(JsonElement::getAsBoolean);
} catch (IndexOutOfBoundsException e) {
return Optional.empty();
}
}
@Override
public Optional optLong(Integer index) {
try {
return Optional.ofNullable(getJsonElement().get(index)).map(JsonElement::getAsLong);
} catch (IndexOutOfBoundsException e) {
return Optional.empty();
}
}
@Override
public Optional optString(Integer index) {
try {
return Optional.ofNullable(getJsonElement().get(index)).map(JsonElement::getAsString);
} catch (IndexOutOfBoundsException e) {
return Optional.empty();
}
}
@Override
public Optional opt(Integer index, Class typeClass) {
try {
return Optional.ofNullable(getJsonElement().get(index)).map(e -> gson.fromJson(e, typeClass));
} catch (JsonSyntaxException e) {
return Optional.empty();
}
}
@Override
public void forEach(BiConsumer entryConsumer) {
for (int i = 0; i < getJsonElement().size(); i++) {
entryConsumer.accept(i, getJsonElement().get(i));
}
}
}
private static class NullExtractor extends JsonExtractor {
NullExtractor(JsonNull jsonElement) {
super(jsonElement);
}
}
private static class PrimitiveExtractor extends JsonExtractor {
PrimitiveExtractor(JsonPrimitive jsonElement) {
super(jsonElement);
}
}
private static class ErrorExtractor extends JsonExtractor {
private final Exception exception;
private ErrorExtractor(Exception exception) {
super(null);
this.exception = exception;
}
@Override
public Builder into(K key) {
return new ErrorBuilder(exception);
}
}
}