正则表达式验证JSON

时间:2010-04-06 08:17:13

标签: regex json

我正在寻找允许我验证json的正则表达式。

我是Regex的新手,我对Regex的解析很糟糕但可以用来验证吗?

12 个答案:

答案 0 :(得分:167)

是的,可以进行完整的正则表达式验证。

大多数现代正则表达式实现都允许递归的regexpressions,它可以验证完整的JSON序列化结构。 json.org specification使其非常简单。

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

使用PCRE functions 在PHP中运行良好。应该在Perl中不做修改;并且当然可以适应其他语言。它也会成功JSON test cases

更简单的RFC4627验证

更简单的方法是RFC4627, section 6中指定的最小一致性检查。然而,它只是作为安全测试和基本的非有效性预防措施:

  var my_JSON_object = !(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test(
         text.replace(/"(\\.|[^"\\])*"/g, ''))) &&
     eval('(' + text + ')');

答案 1 :(得分:26)

是的,正则表达式只能匹配regular languages这是一种常见的误解。事实上, PCRE功能可以比常规语言更多地匹配,它们甚至可以匹配一些非上下文语言! Wikipedia's article on RegExps有一个特殊部分。

可以通过多种方式使用PCRE识别JSON! @mario使用命名子模式和back-references显示了一个很棒的解决方案。然后他指出应该有一个使用recursive patterns (?R)的解决方案。以下是用PHP编写的这种正则表达式的示例:

$regexString = '"([^"\\\\]*|\\\\["\\\\bfnrt\/]|\\\\u[0-9a-f]{4})*"';
$regexNumber = '-?(?=[1-9]|0(?!\d))\d+(\.\d+)?([eE][+-]?\d+)?';
$regexBoolean= 'true|false|null'; // these are actually copied from Mario's answer
$regex = '/\A('.$regexString.'|'.$regexNumber.'|'.$regexBoolean.'|';    //string, number, boolean
$regex.= '\[(?:(?1)(?:,(?1))*)?\s*\]|'; //arrays
$regex.= '\{(?:\s*'.$regexString.'\s*:(?1)(?:,\s*'.$regexString.'\s*:(?1))*)?\s*\}';    //objects
$regex.= ')\Z/is';

我使用(?1)代替(?R),因为后者引用了整个模式,但我们有\A\Z不应在子模式中使用的序列。 (?1)引用最外面括号标记的正则表达式(这就是为什么最外面的( )不以?:开头)。因此,RegExp变为268个字符:)

/\A("([^"\\]*|\\["\\bfnrt\/]|\\u[0-9a-f]{4})*"|-?(?=[1-9]|0(?!\d))\d+(\.\d+)?([eE][+-]?\d+)?|true|false|null|\[(?:(?1)(?:,(?1))*)?\s*\]|\{(?:\s*"([^"\\]*|\\["\\bfnrt\/]|\\u[0-9a-f]{4})*"\s*:(?1)(?:,\s*"([^"\\]*|\\["\\bfnrt\/]|\\u[0-9a-f]{4})*"\s*:(?1))*)?\s*\})\Z/is

无论如何,这应该被视为&#34;技术演示&#34;,而不是一个实际的解决方案。在PHP中,我将通过调用json_decode()函数来验证JSON字符串(就像@Epcylon所说的那样)。如果我要使用那个JSON(如果已经过验证),那么这是最好的方法。

答案 2 :(得分:13)

由于JSON(嵌套{...} - s)的递归特性,正则表达式不适合验证它。当然,一些正则表达式可以递归地匹配模式 * (并且因此可以匹配JSON),但是生成的模式看起来很糟糕,并且永远不应该在生产代码IMO中使用!

* 请注意,许多正则表达式实现支持递归模式。在流行的编程语言中,这些语言支持递归模式:Perl,.NET,PHP和Ruby 1.9.2

答案 3 :(得分:7)

我试过了@ mario的答案,但它对我没有用,因为我已经从JSON.org(archive)下载了测试套件,并且有4个测试失败(fail1.json,fail18.json,fail25.json,fail27.json)。

我调查了错误并发现fail1.json实际上是正确的(根据手册&#39; noteRFC-7159有效字符串也是有效的JSON )。文件fail18.json也不是这样,因为它包含实际上正确的深层嵌套JSON:

[[[[[[[[[[[[[[[[[[[["Too deep"]]]]]]]]]]]]]]]]]]]]

剩下两个文件:fail25.jsonfail27.json

["  tab character   in  string  "]

["line
break"]

两者都包含无效字符。所以我已经像这样更新了模式(更新了字符串子模式):

$pcreRegex = '/
          (?(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* \] )
             (?<pair>      \s* (?&string) \s* : (?&json)  )
             (?<object>    \{  (?:  (?&pair)  (?: , (?&pair)  )*  )?  \s* \} )
             (?<json>   \s* (?: (?&number) | (?&boolean) | (?&string) | (?&array) | (?&object) ) \s* )
          )
          \A (?&json) \Z
          /six';

所以现在可以通过json.org的所有法律测试。

答案 4 :(得分:3)

我创建了一个Mario解决方案的Ruby实现,它可以工作:

# encoding: utf-8

module Constants
  JSON_VALIDATOR_RE = /(
         # define subtypes and build up the json syntax, BNF-grammar-style
         # The {0} is a hack to simply define them as named groups here but not match on them yet
         # I added some atomic grouping to prevent catastrophic backtracking on invalid inputs
         (?<number>  -?(?=[1-9]|0(?!\d))\d+(\.\d+)?([eE][+-]?\d+)?){0}
         (?<boolean> true | false | null ){0}
         (?<string>  " (?>[^"\\\\]* | \\\\ ["\\\\bfnrt\/] | \\\\ u [0-9a-f]{4} )* " ){0}
         (?<array>   \[ (?> \g<json> (?: , \g<json> )* )? \s* \] ){0}
         (?<pair>    \s* \g<string> \s* : \g<json> ){0}
         (?<object>  \{ (?> \g<pair> (?: , \g<pair> )* )? \s* \} ){0}
         (?<json>    \s* (?> \g<number> | \g<boolean> | \g<string> | \g<array> | \g<object> ) \s* ){0}
       )
    \A \g<json> \Z
    /uix
end

########## inline test running
if __FILE__==$PROGRAM_NAME

  # support
  class String
    def unindent
      gsub(/^#{scan(/^(?!\n)\s*/).min_by{|l|l.length}}/u, "")
    end
  end

  require 'test/unit' unless defined? Test::Unit
  class JsonValidationTest < Test::Unit::TestCase
    include Constants

    def setup

    end

    def test_json_validator_simple_string
      assert_not_nil %s[ {"somedata": 5 }].match(JSON_VALIDATOR_RE)
    end

    def test_json_validator_deep_string
      long_json = <<-JSON.unindent
      {
          "glossary": {
              "title": "example glossary",
          "GlossDiv": {
                  "id": 1918723,
                  "boolean": true,
                  "title": "S",
            "GlossList": {
                      "GlossEntry": {
                          "ID": "SGML",
                "SortAs": "SGML",
                "GlossTerm": "Standard Generalized Markup Language",
                "Acronym": "SGML",
                "Abbrev": "ISO 8879:1986",
                "GlossDef": {
                              "para": "A meta-markup language, used to create markup languages such as DocBook.",
                  "GlossSeeAlso": ["GML", "XML"]
                          },
                "GlossSee": "markup"
                      }
                  }
              }
          }
      }
      JSON

      assert_not_nil long_json.match(JSON_VALIDATOR_RE)
    end

  end
end

答案 5 :(得分:2)

查看JSON的文档,如果目标只是检查适应性,那么正则表达式可能只是三个部分:

  1. 字符串以开头,[]{}结尾
    • [{\[]{1} ... [}\]]{1}
    1. 该字符是允许的JSON控制字符(只有一个)
      • ... [,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t] ...
    2. ""中包含的字符集
      • ... ".*?" ...
  2. 所有在一起: [{\[]{1}([,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]|".*?")+[}\]]{1}

    如果JSON字符串包含newline个字符,那么您应该使用正则表达式的singleline开关,以便.匹配newline。请注意,这不会对所有错误的JSON失败,但如果基本JSON结构无效,它将失败,这是在将其传递给解析器之前进行基本健全性验证的直接方式。

答案 6 :(得分:1)

对于“字符串和数字”,我认为数字的部分正则表达式:

-?(?:0|[1-9]\d*)(?:\.\d+)(?:[eE][+-]\d+)?

应该是:

-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+\-]?\d+)?

因为数字的小数部分是可选的,并且在-中转义[+-]符号可能更安全,因为它在括号之间有特殊含义

答案 7 :(得分:1)

JSON数组中的尾随逗号导致我的Perl 5.16挂起,可能是因为它一直在回溯。我不得不添加一个回溯终止指令:

(?<json>   \s* (?: (?&number) | (?&boolean) | (?&string) | (?&array) | (?&object) )(*PRUNE) \s* )
                                                                                   ^^^^^^^^

这样,一旦它识别出不是“可选”(*?)的构造,它就不应该尝试回溯它以尝试将其识别为其他东西。

答案 8 :(得分:0)

如上所述,如果您使用的语言带有JSON库,请使用它来尝试解码字符串并在失败时捕获异常/错误!如果语言没有(只有FreeMarker的这种情况),下面的正则表达式至少可以提供一些非常基本的验证(它为PHP / PCRE编写,可以测试/可用于更多用户)。它并不像公认的解决方案那样万无一失,但也不那么可怕=):

~^\{\s*\".*\}$|^\[\n?\{\s*\".*\}\n?\]$~s

简短说明:

// we have two possibilities in case the string is JSON
// 1. the string passed is "just" a JSON object, e.g. {"item": [], "anotheritem": "content"}
// this can be matched by the following regex which makes sure there is at least a {" at the
// beginning of the string and a } at the end of the string, whatever is inbetween is not checked!

^\{\s*\".*\}$

// OR (character "|" in the regex pattern)
// 2. the string passed is a JSON array, e.g. [{"item": "value"}, {"item": "value"}]
// which would be matched by the second part of the pattern above

^\[\n?\{\s*\".*\}\n?\]$

// the s modifier is used to make "." also match newline characters (can happen in prettyfied JSON)

如果我错过了无意中打破这种情况的话,我很感激你的评论!

答案 9 :(得分:0)


用于验证简单JSON而不是JSONArray的正则表达式

它验证键(string):value(string,integer,[{{key:value},{key:value}],{key:value})

^\{(\s|\n\s)*(("\w*"):(\s)*("\w*"|\d*|(\{(\s|\n\s)*(("\w*"):(\s)*("\w*(,\w+)*"|\d{1,}|\[(\s|\n\s)*(\{(\s|\n\s)*(("\w*"):(\s)*(("\w*"|\d{1,}))((,(\s|\n\s)*"\w*"):(\s)*("\w*"|\d{1,}))*(\s|\n)*\})){1}(\s|\n\s)*(,(\s|\n\s)*\{(\s|\n\s)*(("\w*"):(\s)*(("\w*"|\d{1,}))((,(\s|\n\s)*"\w*"):(\s)*("\w*"|\d{1,}))*(\s|\n)*\})?)*(\s|\n\s)*\]))((,(\s|\n\s)*"\w*"):(\s)*("\w*(,\w+)*"|\d{1,}|\[(\s|\n\s)*(\{(\s|\n\s)*(("\w*"):(\s)*(("\w*"|\d{1,}))((,(\s|\n\s)*"\w*"):(\s)*("\w*"|\d{1,}))*(\s|\n)*\})){1}(\s|\n\s)*(,(\s|\n\s)*\{(\s|\n\s)*(("\w*"):(\s)*(("\w*"|\d{1,}))((,(\s|\n\s)*"\w*"):("\w*"|\d{1,}))*(\s|\n)*\})?)*(\s|\n\s)*\]))*(\s|\n\s)*\}){1}))((,(\s|\n\s)*"\w*"):(\s)*("\w*"|\d*|(\{(\s|\n\s)*(("\w*"):(\s)*("\w*(,\w+)*"|\d{1,}|\[(\s|\n\s)*(\{(\s|\n\s)*(("\w*"):(\s)*(("\w*"|\d{1,}))((,(\s|\n\s)*"\w*"):(\s)*("\w*"|\d{1,}))*(\s|\n)*\})){1}(\s|\n\s)*(,(\s|\n\s)*\{(\s|\n\s)*(("\w*"):(\s)*(("\w*"|\d{1,}))((,(\s|\n\s)*"\w*"):(\s)*("\w*"|\d{1,}))*(\s|\n)*\})?)*(\s|\n\s)*\]))((,(\s|\n\s)*"\w*"):(\s)*("\w*(,\w+)*"|\d{1,}|\[(\s|\n\s)*(\{(\s|\n\s)*(("\w*"):(\s)*(("\w*"|\d{1,}))((,(\s|\n\s)*"\w*"):(\s)*("\w*"|\d{1,}))*(\s|\n)*\})){1}(\s|\n\s)*(,(\s|\n\s)*\{(\s|\n\s)*(("\w*"):(\s)*(("\w*"|\d{1,}))((,(\s|\n\s)*"\w*"):("\w*"|\d{1,}))*(\s|\n)*\})?)*(\s|\n\s)*\]))*(\s|\n\s)*\}){1}))*(\s|\n)*\}$

通过此JSON验证的示例数据

{
"key":"string",
"key": 56,
"key":{
        "attr":"integer",
        "attr": 12
        },
"key":{
        "key":[
            {
                "attr": 4,
                "attr": "string"
            }
        ]
     }
}

答案 10 :(得分:-1)

这里是我的正则表达式验证字符串:

^\"([^\"\\]*|\\(["\\\/bfnrt]{1}|u[a-f0-9]{4}))*\"$

是使用original syntax diagramm撰写的。

答案 11 :(得分:-4)

我意识到这是从6年多前开始的。但是,我认为有一个解决方案,这里没有人提到这比regexing更容易

df$new_column <- +(grepl("mesh|tack|sutur", df$strings, ignore.case = T))