我很惊讶的是,虽然
GsonBuilder#setLenient
声称:
默认情况下,Gson严格遵循RFC 4627中指定的JSON格式。此选项使解析器在接受内容方面更加宽容。
但事实上它总是宽容的。而且,任何对JsonReader.setLenient(false)
的调用都会被完全忽略!
在浏览了众多相关问题和几个被拒绝的拉取请求之后,最终找到了https://github.com/google/gson/issues/1208这个有意义的解决方法:
JakeWharton在2017年12月15日发表评论
您可以调用getAdapter(type).fromJson(gson.newJsonReader(input))而不仅仅是fromJson(input),以获得严格的解析。我们应该真正弃用所有fromJson方法,并添加默认为严格的新版本。
原因是很久以前做出的错误决定,我们现在无法更改 ;(
因此,这里是使用纯Gson解决方案进行严格JSON对象解析的详细测试案例。
import org.junit.Test;
import com.google.gson.*;
import com.google.gson.stream.JsonReader;
import static org.junit.Assert.*;
public class JsonTest {
private static final TypeAdapter<JsonObject> strictGsonObjectAdapter =
new Gson().getAdapter(JsonObject.class);
public static JsonObject parseStrict(String json) {
try {
try (JsonReader reader = new JsonReader(new StringReader(json))) {
JsonObject result = strictGsonObjectAdapter.read(reader);
reader.hasNext();
return result;
}
} catch (IOException e) {
throw new JsonSyntaxException(e);
}
}
@Test
public void testStrictParsing() {
assertThrows(JsonSyntaxException.class, () -> parseStrict("){}"));
assertThrows(JsonSyntaxException.class, () -> parseStrict("]{}"));
assertThrows(JsonSyntaxException.class, () -> parseStrict("}{}"));
assertThrows(JsonSyntaxException.class, () -> parseStrict("{}{}"));
assertThrows(JsonSyntaxException.class, () -> parseStrict("{}[]null"));
assertThrows(JsonSyntaxException.class, () -> parseStrict(""));
assertThrows(JsonSyntaxException.class, () -> parseStrict("null"));
assertThrows(JsonSyntaxException.class, () -> parseStrict("Abracadabra"));
assertThrows(JsonSyntaxException.class, () -> parseStrict("13"));
assertThrows(JsonSyntaxException.class, () -> parseStrict("\"literal\""));
assertThrows(JsonSyntaxException.class, () -> parseStrict("[]"));
assertThrows(JsonSyntaxException.class, () -> parseStrict("{\"number\": NaN}"));
assertThrows(JsonSyntaxException.class, () -> parseStrict("{\"number\": Infinity}"));
assertThrows(JsonSyntaxException.class, () -> parseStrict("{//comment\n}"));
assertThrows(JsonSyntaxException.class, () -> parseStrict("{#comment\n}"));
assertThrows(JsonSyntaxException.class, () -> parseStrict("{/*comment*/}"));
assertThrows(JsonSyntaxException.class, () -> parseStrict("{a: 1}"));
assertThrows(JsonSyntaxException.class, () -> parseStrict("{'a': 1}"));
assertThrows(JsonSyntaxException.class, () -> parseStrict("{\"a\": str}"));
assertThrows(JsonSyntaxException.class, () -> parseStrict("{\"a\": ''}"));
assertThrows(JsonSyntaxException.class, () -> parseStrict("{\"a\": [1;2]}"));
assertThrows(JsonSyntaxException.class, () -> parseStrict("{\"a\": [1,]}"));
assertThrows(JsonSyntaxException.class, () -> parseStrict("{\"a\" = 13}"));
assertThrows(JsonSyntaxException.class, () -> parseStrict("{\"a\" => 13}"));
assertThrows(JsonSyntaxException.class, () -> parseStrict("{\"a\": 1; \"b\": 2}"));
assertThrows(JsonSyntaxException.class, () -> parseStrict("{\"a\": }"));
assertThrows(JsonSyntaxException.class, () -> parseStrict("{\"a\": ,}"));
assertThrows(JsonSyntaxException.class, () -> parseStrict("{\"a\": 0,}"));
assertTrue(parseStrict("{} ").entrySet().isEmpty());
assertTrue(parseStrict("{\"a\": null} \n \n").get("a").isJsonNull());
assertEquals(0, parseStrict("{\"a\": 0}").get("a").getAsInt());
assertEquals("", parseStrict("{\"a\": \"\"}").get("a").getAsString());
assertEquals(0, parseStrict("{\"a\": []}").get("a").getAsJsonArray().size());
}
}
请注意,这确保了单个顶级对象。可以将
JsonObject.class
替换为
JsonArray.class
或
JsonElement.class
,以允许顶级数组或
null。
上面的代码将JSON解析为
JsonObject
DOM表示形式。
下面的代码对常规字段映射进行了严格解析,生成自定义POJO。
private static final TypeAdapter<Pojo> strictGsonAdapter = new Gson().getAdapter(Pojo.class);
public static Pojo parsePayment(String json) throws IOException {
return strictGsonAdapter.fromJson(json);
}