正则expression式来validationJSON
我正在寻找一个正则expression式,允许我validationjson。
对于正则expression式我是非常新的,我知道用正则expression式parsing是不好的,但它可以用来validation?
(这个正则expression式是从那个certificate反对者错误的部门给你带来的。)
是的,一个完整的正则expression式validation是可能的。
大多数现代正则expression式实现允许recursionregexpressions,它可以validation完整的JSON序列化结构。 json.org规范使得它非常简单。
$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 ';
它使用PCREfunction在PHP中运行得非常好。 应该在Perl中不加修改地工作; 当然可以适应其他语言。 而且它也成功地使用了JSONtesting用例 。
更简单的RFC4627validation
更简单的方法是RFC4627第6节中规定的最小一致性检查。 然而,它只是作为安全testing和基本的无效预防措施:
var my_JSON_object = !(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test( text.replace(/"(\\.|[^"\\])*"/g, ''))) && eval('(' + text + ')');
是的,正则expression式只能匹配常规语言是一个常见的误解。 事实上, PCRE函数可以比常规语言匹配更多 ,甚至可以匹配一些非上下文无关的语言! 关于RegExps的维基百科文章有一个关于它的特殊部分。
JSON可以通过几种方式使用PCRE进行识别! @mario使用命名的子模式和反向引用显示了一个很好的解决scheme。 然后他指出,应该有一个使用recursion模式 (?R)
的解决scheme。 这里是用PHP编写的这样的正则expression式的例子:
$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)
引用由最外括号标记的正则expression式(这就是为什么outermost ( )
不以?:
开头)。 所以,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
无论如何,这应该被视为一个“技术示范”,而不是一个实际的解决scheme。 在PHP中,我将通过调用json_decode()
函数来validationJSONstring(就像@Epcylon指出的那样)。 如果我要使用该JSON(如果validation),那么这是最好的方法。
由于JSON(嵌套{...}
-s)的recursion性质,正则expression式不适合validation它。 当然,有些正则expression式可以recursion地匹配模式* (因此可以匹配JSON),但是结果模式是可怕的,并且永远不会用在生产代码IMO中!
*当心,许多正则expression式实现不支持recursion模式。 在stream行的编程语言中,这些支持recursion模式:Perl,.NET,PHP和Ruby 1.9.2
我试了@马里奥的答案,但它不适合我,因为我从JSON.org( 归档 )下载了testing套件,并有4个失败的testing(fail1.json,fail18.json,fail25.json,fail27。 JSON)。
我调查了错误,发现fail1.json
实际上是正确的(根据手册的说明和RFC-7159有效的string也是有效的JSON)。 文件fail18.json
也不是这样,因为它实际上包含了正确的深层嵌套的JSON:
[[[[[[[[[[[[[[[[[[[["Too deep"]]]]]]]]]]]]]]]]]]]]
所以剩下两个文件: fail25.json
和fail27.json
:
[" tab character in string "]
和
["line break"]
两者都包含无效字符。 所以我更新了这样的模式(string子模式更新):
$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的合法testing都可以通过。
我创build了一个Mario解决scheme的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
对于“string和数字”,我认为数字的部分正则expression式:
-?(?:0|[1-9]\d*)(?:\.\d+)(?:[eE][+-]\d+)?
应该是:
-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+\-]?\d+)?
由于该数字的小数部分是可选的,因此它可能更安全一些,因为它在括号之间有特殊含义
JSON数组尾随的逗号导致我的Perl 5.16挂起,可能是因为它保持回溯。 我不得不添加一个回溯终止指令:
(?<json> \s* (?: (?&number) | (?&boolean) | (?&string) | (?&array) | (?&object) )(*PRUNE) \s* ) ^^^^^^^^
这样,一旦它识别出一个不是“可选的”( *
或?
)的构造,就不应该试着回溯它,试图将其识别为别的东西。
如上所述,如果您使用的语言带有JSON库,请使用它来尝试对string进行解码,如果失败,则会捕获exception/错误! 如果语言没有(FreeMarker只有这样的情况),下面的正则expression式至less可以提供一些非常基本的validation(它是为PHP / PCRE编写的,可供更多用户testing/使用)。 它不像被接受的解决scheme那样简单,但也不那么吓人=):
~^\{\s*\".*\}$|^\[\n?\{\s*\".*\}\n?\]$~s
简短的解释:
// we have two possibilities in case the string is JSON // 1. the string passed is "just" a JSON object, eg {"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, eg [{"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)
如果我错过了会无意中破坏的东西,我很感激您的意见!
看看JSON的文档,如果目标只是检查健身,似乎正则expression式可能只是三个部分:
- 该string以
[]
或{}
开头和结尾-
[{\[]{1}
…[}\]]{1}
-
- 和
- 该字符是一个允许的JSON控制字符(只有一个)
- …
[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]
…
- …
- 或者包含在
""
中的一组字符- …
".*?"
…
- …
- 该字符是一个允许的JSON控制字符(只有一个)
全部在一起: [{\[]{1}([,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]|".*?")+[}\]]{1}
如果JSONstring包含newline
,那么你应该在你的正则expression式中使用singleline
开关.
匹配newline
。 请注意,这不会在所有错误的JSON上失败,但是如果基本的JSON结构是无效的,它将会失败,这是在将其传递给parsing器之前进行基本健全性validation的直接方法。
这里是我validationstring的正则expression式:
^\"([^\"\\]*|\\(["\\\/bfnrt]{1}|u[a-f0-9]{4}))*\"$
写了原始的语法diagramm 。
我意识到这是来自六年多前。 不过,我认为有一个解决scheme,没有人提到这个方法比regexing更容易
function isAJSON(string) { try { JSON.parse(string) } catch(e) { if(e instanceof SyntaxError) return false; }; return true; }