Regex, чтобы найти, если частичный ввод действителен JSON - PullRequest
3 голосов
/ 07 февраля 2020

У меня есть сценарий, в котором мне нужно проверить, действителен ли частичный ввод (см. Ниже) JSON или нет? Я сослался на этот ответ , чтобы определить, является ли данная строка действительной JSON или нет.

Пример ввода:

 { 
 "JSON": [{
      "foo":"bar",
      "details": {
           "name":"bar",
           "id":"bar",

Что я пробовал до сих пор:

/ (?(DEFINE)
         (?<number>   -? (?= [1-9]|0(?!\d) ) \d+ (\.\d+)? ([eE] [+-]? \d+)? )
         (?<boolean>   true | false | null )
         (?<string>    " ([^"\n\r\t\\\\]* | \\\\ ["\\\\bfnrt\/] | \\\\ u [0-9a-f]{4} )* " )
         (?<array>     \[  (?:  (?&json)  (?: , (?&json)  )*  )?  \s* \]{0,1} )
         (?<pair>      \s* (?&string) \s* : (?&json)  )
         (?<object>    \{  (?:  (?&pair)  (?: , (?&pair)  )*  )?  \s* \}{0,1} )
         (?<json>   \s* (?: (?&number) | (?&boolean) | (?&string) | (?&array) | (?&object) ) \s* )
) \A (?&json)\,{0,1} \Z /six

Я сделал закрытие массива и объектов необязательным (допускается ноль или один раз ). Но в некоторых случаях это не удастся, например, когда вы открываете объект, не закрывая другой объект (как показано ниже), регулярное выражение все равно найдет совпадение.

Неправильно, но все еще совпадает:

 { 
 "JSON": [{
      "foo":"bar",
      "details": {
           "name":"bar",
           "id":"bar",{

Как проверить частичный JSON ввод?

РЕДАКТИРОВАТЬ:

Как отмечено @ntahdh в комментариях, это регулярное выражение не будет работать с java.util.regex. Так что теперь мне нужно регулярное выражение, которое должно работать без рекурсии

Ответы [ 3 ]

6 голосов
/ 08 февраля 2020

Это не вполне ответ на ваш вопрос и был бы, если бы форма комментария была бы достаточной для этого числа символов.

JSON не обычный язык и поэтому не может быть распознан только механизмом регулярных выражений (если вы программируете на Python, пакет regex предоставляет расширения, которые могут позволить выполнить sh вашу задачу, но я сказал, что обычно true).

Если генератор синтаксического анализатора недоступен для вашего предпочтительного языка, вы можете рассмотреть возможность создания простого синтаксического анализатора с рекурсивным спуском. Уже определенные вами регулярные выражения хорошо послужат для создания токенов, которые будут входными данными для этого анализатора. Конечно, вы ожидаете, что произойдет ошибка синтаксического анализа, но она должна возникать на входном токене, являющемся токеном конца файла. Ошибка синтаксического анализа, возникающая до сканирования маркера конца файла, предполагает, что у не есть префикс действительного JSON. Если вы работаете с анализатором «снизу вверх» и «сдвиг-уменьшение», например, сгенерированным с помощью YA CC, то это будет ошибкой сдвига на чем-то отличном от маркера конца файла.

1 голос
/ 26 февраля 2020

Я знаю, что использовать регулярные выражения для проверки некоторых строк с вложенными структурами нелегко, если не вообще невозможно. Вероятно, у вас будет больше шансов использовать существующий парсер JSON.

Использование стека для отслеживания все еще открытых объектов и массивов. Добавьте необходимые закрывающие фигурные и квадратные скобки. Спросите у синтаксического анализатора JSON, является ли ваша новая строка действительной JSON.

Возможно, вам придется проделать определенную работу, чтобы обрабатывать запятые и кавычки, но вы поняли.

С примером кода:

import com.google.gson.JsonParser;
import com.google.gson.JsonSyntaxException;

import java.util.Stack;

public class Main {
    public static void main(String[] args) {
        String valid = "{\n" +
                       "\"JSON\": [{\n" +
                       "    \"foo\":\"bar\",\n" +
                       "    \"details\": {\n" +
                       "         \"name\":\"bar\",\n" +
                       "         \"id\":\"bar\"";
        System.out.println("Is valid?:\n" + valid + "\n" + validate(valid));

        String invalid = "{ \n" +
                         " \"JSON\": [{\n" +
                         "      \"foo\":\"bar\",\n" +
                         "      \"details\": {\n" +
                         "           \"name\":\"bar\",\n" +
                         "           \"id\":\"bar\",{";
        System.out.println("Is valid?:\n" + invalid + "\n" + validate(invalid));
    }

    public static boolean validate(String input) {
        Stack<String> closings = new Stack<>();

        for (char ch: input.toCharArray()) {
            switch(ch) {
                case '{':
                    closings.push("}");
                    break;
                case '[':
                    closings.push("]");
                    break;
                case '}':
                case ']':
                    closings.pop();
            }
        }

        StringBuilder closingBuilder = new StringBuilder();
        while (! closings.empty()) {
            closingBuilder.append(closings.pop());
        }
        String fullInput = input + closingBuilder.toString();
        JsonParser parser = new JsonParser();
        try{
            parser.parse(fullInput);
        }
        catch(JsonSyntaxException jse){
            return false;
        }
        return true;
    }
}

Что приводит к:

Is valid?:
{
"JSON": [{
    "foo":"bar",
    "details": {
         "name":"bar",
         "id":"bar"
true
Is valid?:
{ 
 "JSON": [{
      "foo":"bar",
      "details": {
           "name":"bar",
           "id":"bar",{
false

Обратите внимание, что добавление запятой после строки "bar" в допустимом примере делает его недействительным (потому что "bar",}]}} является недействительным JSON).

1 голос
/ 25 февраля 2020

почему бы не позволить парсеру вроде Gson сделать это за вас, вы в основном имеете дело с потоком и на уровне токенов.

import java.io.IOException;
import java.io.StringReader;

import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;

public class Main 
{
    public static void main(String[] args) throws Exception 
    {

        String json = "{'id': 1001,'firstName': 'Lokesh','lastName': 'Gupta','email': null}";

        JsonReader jsonReader = new JsonReader(new StringReader(json));
        jsonReader.setLenient(true);

        try
        {
            while (jsonReader.hasNext()) 
            {
                JsonToken nextToken = jsonReader.peek();

                if (JsonToken.BEGIN_OBJECT.equals(nextToken)) {

                    jsonReader.beginObject();

                } else if (JsonToken.NAME.equals(nextToken)) {

                    String name = jsonReader.nextName();
                    System.out.println("Token KEY >>>> " + name);

                } else if (JsonToken.STRING.equals(nextToken)) {

                    String value = jsonReader.nextString();
                    System.out.println("Token Value >>>> " + value);

                } else if (JsonToken.NUMBER.equals(nextToken)) {

                    long value = jsonReader.nextLong();
                    System.out.println("Token Value >>>> " + value);

                } else if (JsonToken.NULL.equals(nextToken)) {

                    jsonReader.nextNull();
                    System.out.println("Token Value >>>> null");

                } else if (JsonToken.END_OBJECT.equals(nextToken)) {

                    jsonReader.endObject();

                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            jsonReader.close();
        }
    }
}

источник: https://howtodoinjava.com/gson/jsonreader-streaming-json-parser/

...