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

yzckvree  于 2022-11-06  发布在  Java
关注(0)|答案(6)|浏览(181)

我在How to check whether a given string is valid JSON in 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;
        }
    }

如果我用这个方法处理某个字符串,它总是返回true。例如:

System.out.println(renderHtml.isJson("{\"status\": \"UP\"}"));

它给了我true

System.out.println(renderHtml.isJson("bncjbhjfjhj"));

也给了我true

z9zf31ra

z9zf31ra1#

您不应该使用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());

输出量:
谷歌公司网站首页
类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;
    }
}

然后测试它:

System.out.println(isJsonValid(VALID_JSON));
System.out.println(isJsonValid(INVALID_JSON));

输出量:
真的
假的

qaxu7uf2

qaxu7uf22#

我找到了解决方案,但使用org.json库,根据如何检查给定字符串是否为Java中的有效JSON

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

现在随机字符串bncjbhjfjhjfalse{"status": "UP"}是真。

xiozqbni

xiozqbni3#

虽然你可能觉得奇怪

"bncjbhjfjhj"

确实是有效的json,因为它是一个字符串,并且是它的唯一字符串。
根据不那么新的JSON RFC
JSON文本是一个序列化的值。请注意,某些以前的JSON规范将JSON文本限制为对象或数组。在调用JSON文本的地方只生成对象或数组的实现将是可互操作的,因为所有实现都将接受这些对象或数组作为一致的JSON文本。

eanckbw9

eanckbw94#

我很惊讶当GsonBuilder#setLenient声明
默认情况下,Gson是严格的,只接受RFC 4627指定的JSON。这个选项使解析器可以自由接受。
它看起来是彻头彻尾的谎言,实际上它总是很宽容。而且,甚至任何对JsonReader.setLenient(false)的调用都被完全忽略!
在浏览了一些与numerous相关的issuesseveralrejectedpull请求后,“由于传统的兼容性原因”,我终于找到了具有合理解决方案的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) {
        // https://stackoverflow.com/questions/43233898/how-to-check-if-json-is-valid-in-java-using-gson/47890960#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());
    }

}

请注意,这确保了单个顶级对象。可以将JsonObject.class替换为JsonArray.classJsonElement.class,以允许顶级数组或null
上面的代码将JSON解析为JsonObject DOM表示。
下面的代码使用常规字段Map对自定义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);
}
t5fffqht

t5fffqht5#

这对我有用

public static boolean isJson(String Json) {
    Gson gson = new Gson();
    try {
        gson.fromJson(Json, Object.class);
        Object jsonObjType = gson.fromJson(Json, Object.class).getClass();
        if(jsonObjType.equals(String.class)){
            return false;
        }
        return true;
    } catch (com.google.gson.JsonSyntaxException ex) {
        return false;
    }
}
eivnm1vs

eivnm1vs6#

如果您只想验证输入是否为有效的JSON,而不使用解析后的JSON数据,那么最简单且可能性能最高的解决方案是:

public static boolean isValidJson(String input) {
    try (JsonReader reader = new JsonReader(new StringReader(input))) {
        reader.skipValue();
        return reader.peek() == JsonToken.END_DOCUMENT;
    } catch (IOException e) {
        return false;
    }
}

但是请注意,即使JsonReader在非宽松模式下也允许某些根据规范无效的JSON字符串,请参见JsonReader.setLenient(boolean)的文档,其中列出了这些情况。
使用GsonJsonParser的所有其他解决方案可能无法正常工作,因为这些类在默认情况下是宽松的,并且无法配置此行为。

相关问题