正则表达式查找部分输入是不是为有效 JSON

Posted

技术标签:

【中文标题】正则表达式查找部分输入是不是为有效 JSON【英文标题】:Regex to find if the partial input is valid JSON正则表达式查找部分输入是否为有效 JSON 【发布时间】:2020-05-23 13:58:55 【问题描述】:

我有一个场景,我需要验证部分输入(见下文)是否是有效的 JSON?我已经引用了这个answer 来确定给定的字符串是否是有效的 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 在 cmets 中所提到的,此正则表达式无法使用 java.util.regex。所以现在我需要一个不需要递归的正则表达式

【问题讨论】:

您要解决的核心业务用例和需求是什么? 我只是想从输入流中读取和解析多个 JSON 对象,我无法完全读取并解析它,因为流大小是未知的。 如果第一部分包含有效的 JSON 但后面的部分包含损坏的 JSON 怎么办?这是您关心的用例吗? 保证如果第一部分是JSON,那么剩下的就是有效的JSON对象 我不确定你使用的PCRE库的Java接口是否支持,但是这里可以使用原PCRE库中的pcrepartial。它将检查是否已到达字符串的末尾(因此到目前为止的部分字符串是有效的)。您可以使用硬部分匹配或软部分匹配 - 在这里没关系,因为我们总是假设数据不完整。 【参考方案1】:

这不是相当对您问题的回答,如果允许的字符数足够的话,如果评论的形式是这样的话。

JSON 不是常规语言,因此不能仅由正则表达式引擎识别(如果您使用 Python 编程,regex 包提供的扩展可能使您能够完成您的任务,但我所说的是一般正确)。

如果解析器生成器不适用于您的首选语言,您可以考虑创建一个简单的递归下降解析器。您已经定义的正则表达式将很好地为您创建将成为该解析器输入的标记。当然,您会期望会发生解析错误——但它应该发生在作为文件结束标记的输入标记上。在扫描文件结束标记之前发生的解析错误表明您确实 not 具有有效 JSON 的前缀。如果您使用的是自下而上的 shift-reduce 解析器,例如使用 YACC 生成的解析器,那么这将是文件结尾标记以外的其他内容的移位错误。

【讨论】:

该问题已被标记为 PCRE,它有足够的能力进行括号匹配。不过,尝试使用正则表达式来实现这一点并不是一个好主意。 @nhahtdh : PCRE 提供了一种语言规范,但有几十种风格......存在递归模式,这是真的,但并非所有 PCRE 风格都支持 (?R) 或平衡组。 @DavidAmar PCRE 是语言规范吗?我发现与实际规范相比,这些文档不太正式,例如在 javascript 中,它详细描述了匹配的工作原理。 PCRE 只是一个在 Perl 中实现正则表达式语法的正则表达式库(在众多库中),尽管 PCRE 特别试图与 Perl 兼容,而其他库仅实现 Perl 中正则表达式语法的一个子集。【参考方案2】:

为什么不让像 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/

【讨论】:

OP' 正在寻找一种方法来验证部分 JSON。 Gson 验证完整的 JSON。那就不能用了。 不只是一个对象,是多个对象的流,不保证在这个验证过程中得到整个对象【参考方案3】:

我知道使用正则表达式来验证一些带有嵌套结构的字符串并不容易,如果不是完全不可行的话。 您可能会有更多机会使用现有的 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)。

【讨论】:

以上是关于正则表达式查找部分输入是不是为有效 JSON的主要内容,如果未能解决你的问题,请参考以下文章

notepad正则怎么替换其中一部分

SQLite正则表达式用于查找字符串不在预定义集中的行

使用正则表达式查找哈希表/字典/地图

输入类型=“数字”时,正则表达式验证是不是有效?

正则表达式查找带空格的整数中的无效字符

Java 正则表达式将字符串转换为有效的 json 字符串