java json语法正则检查_正则表达式验证JSON

正则表达式验证JSON

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

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

Shard asked 2019-04-30T15:59:19Z

11个解决方案

160 votes

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

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

$pcre_regex = '

/

(?(DEFINE)

(? -? (?= [1-9]|0(?!\d) ) \d+ (\.\d+)? ([eE] [+-]? \d+)? )

(? true | false | null )

(? " ([^"\\\\]* | \\\\ ["\\\\bfnrt\/] | \\\\ u [0-9a-f]{4} )* " )

(? \[ (?: (?&json) (?: , (?&json) )* )? \s* \] )

(? \s* (?&string) \s* : (?&json) )

(? \{ (?: (?&pair) (?: , (?&pair) )* )? \s* \} )

(? \s* (?: (?&number) | (?&boolean) | (?&string) | (?&array) | (?&object) ) \s* )

)

\A (?&json) \Z

/six

';

它在PHP中与PCRE功能相当不错。 应该在Perl中不做修改; 并且当然可以适应其他语言。 它也可以成功使用JSON测试用例。

更简单的RFC4627验证

一种更简单的方法是RFC4627第6节中规定的最小一致性检查。但它只是作为安全测试和基本的非有效性预防措施:

var my_JSON_object = !(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test(

text.replace(/"(\\.|[^"\\])*"/g, ''))) &&

eval('(' + text + ')');

mario answered 2019-04-30T16:00:05Z

25 votes

是的,正则表达式只能匹配常规语言,这是一种常见的误解。 事实上,PCRE功能可以比常规语言更匹配,甚至可以匹配一些非上下文语言! 维基百科关于RegExps的文章有一个特殊的部分。

可以通过多种方式使用PCRE识别JSON! @mario使用命名子模式和反向引用显示了一个很好的解决方案。 然后他指出应该有一个使用递归模式的解决方案(?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';

我使用的是json_decode()而不是(?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

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

Hrant Khachatrian answered 2019-04-30T16:00:56Z

13 votes

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

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

Bart Kiers answered 2019-04-30T16:01:31Z

5 votes

我尝试了@ mario的答案,但它对我不起作用,因为我从JSON.org(存档)下载了测试套件,并且有4个失败的测试(fail1.json,fail18.json,fail25.json,fail27。JSON)。

我已经调查了错误并发现,fail1.json实际上是正确的(根据手册的说明和RFC-7159有效字符串也是一个有效的JSON)。 文件fail25.json也不是这样,因为它包含实际上正确的深层嵌套JSON:

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

剩下两个文件:fail25.json和fail27.json:

[" tab character in string "]

["line

break"]

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

$pcreRegex = '/

(?(DEFINE)

(? -? (?= [1-9]|0(?!\d) ) \d+ (\.\d+)? ([eE] [+-]? \d+)? )

(? true | false | null )

(? " ([^"\n\r\t\\\\]* | \\\\ ["\\\\bfnrt\/] | \\\\ u [0-9a-f]{4} )* " )

(? \[ (?: (?&json) (?: , (?&json) )* )? \s* \] )

(? \s* (?&string) \s* : (?&json) )

(? \{ (?: (?&pair) (?: , (?&pair) )* )? \s* \} )

(? \s* (?: (?&number) | (?&boolean) | (?&string) | (?&array) | (?&object) ) \s* )

)

\A (?&json) \Z

/six';

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

Gino Pane answered 2019-04-30T16:02:33Z

2 votes

我创建了一个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

(? -?(?=[1-9]|0(?!\d))\d+(\.\d+)?([eE][+-]?\d+)?){0}

(? true | false | null ){0}

(? " (?>[^"\\\\]* | \\\\ ["\\\\bfnrt\/] | \\\\ u [0-9a-f]{4} )* " ){0}

(? \[ (?> \g (?: , \g )* )? \s* \] ){0}

(? \s* \g \s* : \g ){0}

(? \{ (?> \g (?: , \g )* )? \s* \} ){0}

(? \s* (?> \g | \g | \g | \g | \g ) \s* ){0}

)

\A \g \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 = <

{

"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

pmarreck answered 2019-04-30T16:03:01Z

1 votes

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

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

应该是:

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

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

Mikaeru answered 2019-04-30T16:03:45Z

1 votes

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

(? \s* (?: (?&number) | (?&boolean) | (?&string) | (?&array) | (?&object) )(*PRUNE) \s* )

^^^^^^^^

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

user117529 answered 2019-04-30T16:04:22Z

1 votes

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

字符串以newline或singleline开头和结尾newline...newline

和该字符是允许的JSON控制字符(只有一个)...newline...

或newline中包含的字符集...newline...

全部一起:newline

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

cjbarth answered 2019-04-30T16:06:00Z

0 votes

如上所述,如果您使用的语言带有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)

如果我错过了无意中打破这个的东西,我很感激你的评论!

exside answered 2019-04-30T16:06:48Z

-1 votes

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

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

是使用原始语法图编写的。

gobwas answered 2019-04-30T16:07:27Z

-4 votes

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

function isAJSON(string) {

try {

JSON.parse(string)

} catch(e) {

if(e instanceof SyntaxError) return false;

};

return true;

}

Jamie answered 2019-04-30T16:08:00Z

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值