如何使用GSON检查JSON在Java中是否有效?

Qki*_*kiZ 8 java json gson

我有方法必须检查JSON是否有效,可以在如何检查给定字符串是否是Java中的有效JSON但是它不起作用.

public static boolean isJson(String Json) {
        Gson gson = new Gson();
        try {
            gson.fromJson(Json, Object.class);
            return true;
        } catch (com.google.gson.JsonSyntaxException ex) {
            return false;
        }
    }
Run Code Online (Sandbox Code Playgroud)

如果我将这个方法与一些字符串一起使用,它总是返回true.例如:

System.out.println(renderHtml.isJson("{\"status\": \"UP\"}"));
Run Code Online (Sandbox Code Playgroud)

它给了我true,而且

System.out.println(renderHtml.isJson("bncjbhjfjhj"));
Run Code Online (Sandbox Code Playgroud)

true也给了我.

Wil*_*Gfx 8

虽然这对你来说可能很奇怪

"bncjbhjfjhj"
Run Code Online (Sandbox Code Playgroud)

确实是有效的json,因为它是一个字符串,它是唯一的字符串.

根据不那么新的JSON RFC

JSON文本是序列化值.请注意,某些先前的JSON规范将JSON文本约束为对象或数组.只生成调用JSON文本的对象或数组的实现将是可互操作的,因为所有实现都将接受这些作为符合JSON文本.

  • OP用`renderHtml.isJson("bncjbhjfjhj")`测试它,而不是`renderHtml.isJson("\"bncjbhjfjhj \"")`. (2认同)

Lyu*_*riv 8

您不应该使用Gson这种验证:

  • Gson是执行反序列化的对象,因此它将整个 JSON 反序列化为内存中的对象。
  • Gson,但我不知道,对于某些无效的JSON来说可能不是很严格:bncjbhjfjhj作为一个java.lang.String实例反序列化。惊喜!
private static final Gson gson = new Gson();

private static final String VALID_JSON = "{\"status\": \"UP\"}";
private static final String INVALID_JSON = "bncjbhjfjhj";

System.out.println(gson.fromJson(VALID_JSON, Object.class).getClass());
System.out.println(gson.fromJson(INVALID_JSON, Object.class).getClass());
Run Code Online (Sandbox Code Playgroud)

输出:

com.google.gson.internal.LinkedTreeMap
类java.lang.String类

您可以在这里使用的方法JsonReader是逐个令牌地读取传入的JSON令牌,从而确定给定的JSON文档在语法上是否有效。

private static boolean isJsonValid(final String json)
        throws IOException {
    return isJsonValid(new StringReader(json));
}

private static boolean isJsonValid(final Reader reader)
        throws IOException {
    return isJsonValid(new JsonReader(reader));
}

private static boolean isJsonValid(final JsonReader jsonReader)
        throws IOException {
    try {
        JsonToken token;
        loop:
        while ( (token = jsonReader.peek()) != END_DOCUMENT && token != null ) {
            switch ( token ) {
            case BEGIN_ARRAY:
                jsonReader.beginArray();
                break;
            case END_ARRAY:
                jsonReader.endArray();
                break;
            case BEGIN_OBJECT:
                jsonReader.beginObject();
                break;
            case END_OBJECT:
                jsonReader.endObject();
                break;
            case NAME:
                jsonReader.nextName();
                break;
            case STRING:
            case NUMBER:
            case BOOLEAN:
            case NULL:
                jsonReader.skipValue();
                break;
            case END_DOCUMENT:
                break loop;
            default:
                throw new AssertionError(token);
            }
        }
        return true;
    } catch ( final MalformedJsonException ignored ) {
        return false;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后测试一下:

System.out.println(isJsonValid(VALID_JSON));
System.out.println(isJsonValid(INVALID_JSON));
Run Code Online (Sandbox Code Playgroud)

输出:



Vad*_*zim 8

我很惊讶虽然GsonBuilder#setLenient声明

默认情况下,Gson 是严格的,仅接受RFC 4627指定的 JSON 。此选项使解析器可以自由地接受它。

这看起来是彻头彻尾的谎言,但实际上总是很宽容。而且,甚至任何调用都JsonReader.setLenient(false)被完全忽略!

在浏览了许多 相关 问题几个 “由于遗留兼容性原因”而被拒绝的 拉取请求之后,我终于找到了https://github.com/google/gson/issues/1208以及合理的解决方法:

杰克沃顿于 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) {
        // /sf/ask/3026372891/#47890960
        try {
            //return strictGsonObjectAdapter.fromJson(json); // this still allows multiple top level values (
            try (JsonReader reader = new JsonReader(new StringReader(json))) {
                JsonObject result = strictGsonObjectAdapter.read(reader);
                reader.hasNext(); // throws on multiple top level values
                return result;
            }
        } catch (IOException e) {
            throw new JsonSyntaxException(e);
        }
    }

    @Test
    public void testStrictParsing() {
        // https://static.javadoc.io/com.google.code.gson/gson/2.8.5/com/google/gson/stream/JsonReader.html#setLenient-boolean-
        // Streams that start with the non-execute prefix, ")]}'\n".
        assertThrows(JsonSyntaxException.class, () -> parseStrict("){}"));
        assertThrows(JsonSyntaxException.class, () -> parseStrict("]{}"));
        assertThrows(JsonSyntaxException.class, () -> parseStrict("}{}"));
        // Streams that include multiple top-level values. With strict parsing, each stream must contain exactly one top-level value.
        assertThrows(JsonSyntaxException.class, () -> parseStrict("{}{}"));
        assertThrows(JsonSyntaxException.class, () -> parseStrict("{}[]null"));
        // Top-level values of any type. With strict parsing, the top-level value must be an object or an array.
        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("[]"));
        // Numbers may be NaNs or infinities.
        assertThrows(JsonSyntaxException.class, () -> parseStrict("{\"number\": NaN}"));
        assertThrows(JsonSyntaxException.class, () -> parseStrict("{\"number\": Infinity}"));
        // End of line comments starting with // or # and ending with a newline character.
        assertThrows(JsonSyntaxException.class, () -> parseStrict("{//comment\n}"));
        assertThrows(JsonSyntaxException.class, () -> parseStrict("{#comment\n}"));
        // C-style comments starting with /* and ending with */. Such comments may not be nested.
        assertThrows(JsonSyntaxException.class, () -> parseStrict("{/*comment*/}"));
        // Names that are unquoted or 'single quoted'.
        assertThrows(JsonSyntaxException.class, () -> parseStrict("{a: 1}"));
        assertThrows(JsonSyntaxException.class, () -> parseStrict("{'a': 1}"));
        // Strings that are unquoted or 'single quoted'.
        assertThrows(JsonSyntaxException.class, () -> parseStrict("{\"a\": str}"));
        assertThrows(JsonSyntaxException.class, () -> parseStrict("{\"a\": ''}"));
        // Array elements separated by ; instead of ,.
        assertThrows(JsonSyntaxException.class, () -> parseStrict("{\"a\": [1;2]}"));
        // Unnecessary array separators. These are interpreted as if null was the omitted value.
        assertThrows(JsonSyntaxException.class, () -> parseStrict("{\"a\": [1,]}"));
        // Names and values separated by = or => instead of :.
        assertThrows(JsonSyntaxException.class, () -> parseStrict("{\"a\" = 13}"));
        assertThrows(JsonSyntaxException.class, () -> parseStrict("{\"a\" => 13}"));
        // Name/value pairs separated by ; instead of ,.
        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());
    }

}
Run Code Online (Sandbox Code Playgroud)

请注意,这确保了单个顶级对象。可以替换JsonObject.classJsonArray.classJsonElement.class允许顶级数组或null

上面的代码将 JSON 解析为JsonObjectDOM 表示。

下面的代码使用常规字段映射严格解析为自定义 POJO。

// https://github.com/google/gson/issues/1208
private static final TypeAdapter<Pojo> strictGsonAdapter = new Gson().getAdapter(Pojo.class);

public static Pojo parsePayment(String json) throws IOException {
    return strictGsonAdapter.fromJson(json);
}
Run Code Online (Sandbox Code Playgroud)


Qki*_*kiZ 7

我找到了解决方案,但使用org.json库,根据How to check if a given string is valid JSON in Java

public static boolean isJson(String Json) {
        try {
            new JSONObject(Json);
        } catch (JSONException ex) {
            try {
                new JSONArray(Json);
            } catch (JSONException ex1) {
                return false;
            }
        }
        return true;
    }
Run Code Online (Sandbox Code Playgroud)

现在看起来随机的字符串bncjbhjfjhjfalse并且{"status": "UP"}是真的。