json의 유효성을 검사 할 수있는 Regex를 찾고 있습니다.
저는 Regex를 처음 접했고 Regex를 사용한 구문 분석이 나쁘다는 것을 충분히 알고 있지만 유효성 검사에 사용할 수 있습니까?
json의 유효성을 검사 할 수있는 Regex를 찾고 있습니다.
저는 Regex를 처음 접했고 Regex를 사용한 구문 분석이 나쁘다는 것을 충분히 알고 있지만 유효성 검사에 사용할 수 있습니까?
답변:
대부분의 최신 정규식 구현은 완전한 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
';
그것은 PCRE 기능을 가진 PHP에서 아주 잘 작동 합니다 . Perl에서 수정하지 않고 작동해야합니다. 다른 언어에도 적용 할 수 있습니다. 또한 JSON 테스트 케이스 에서도 성공합니다 .
더 간단한 접근 방식은 RFC4627, 섹션 6에 지정된 최소 일관성 검사 입니다. 그러나 이는 보안 테스트 및 기본 비유 효성 예방 조치 일뿐입니다.
var my_JSON_object = !(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test(
text.replace(/"(\\.|[^"\\])*"/g, ''))) &&
eval('(' + text + ')');
false
일치하는 반면 최상위 JSON 값은 배열 또는 객체 여야합니다. 또한 문자열이나 공백에서 허용되는 문자 집합에 많은 문제가 있습니다.
예, 정규 표현식이 정규 언어 와 만 일치 할 수 있다는 것은 일반적인 오해입니다 . 실제로 PCRE 함수는 일반 언어보다 훨씬 더 많이 일치 할 수 있으며 컨텍스트가없는 일부 언어 와도 일치 할 수 있습니다! RegExps 에 대한 Wikipedia의 기사 에는 그것에 대한 특별한 섹션이 있습니다.
JSON은 PCRE를 사용하여 여러 가지 방법으로 인식 할 수 있습니다! @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';
내가 사용하고 (?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
어쨌든 이것은 실질적인 해결책이 아니라 "기술 데모"로 취급되어야합니다. PHP에서 json_decode()
함수 를 호출하여 JSON 문자열의 유효성을 검사합니다 (@Epcylon이 언급 한 것처럼). 해당 JSON 을 사용 하려는 경우 (검증 된 경우) 이것이 가장 좋은 방법입니다.
\d
은 위험합니다. 많은 정규 표현식 구현 \d
에서 숫자의 유니 코드 정의와 일치 할 [0-9]
뿐만 아니라 대체 스크립트를 포함합니다.
\d
PHP의 PCRE 구현에서 유니 코드 번호와 일치하지 않는다고 생각 합니다. 예를 들어 ٩
심볼 (0x669 아랍어 인도어 구 자리) 패턴을 이용하여 정합 될 #\p{Nd}#u
수 있지만#\d#u
/u
플래그를 사용하지 않았기 때문이 아닙니다 . JSON은 UTF-8로 인코딩됩니다. 적절한 정규 표현식의 경우 해당 플래그를 사용해야합니다.
u
수정자를 사용 했습니다. 이전 댓글의 패턴을 다시 살펴보십시오. :) 문자열, 숫자 및 부울은 최상위 수준에서 올바르게 일치합니다. 여기 긴 정규 표현식 붙여 넣을 수 있습니다 quanetic.com/Regex을 자신을 시도
JSON (중첩 된 {...}
-s) 의 재귀 적 특성으로 인해 정규식은 유효성을 검사하는 데 적합하지 않습니다. 물론 일부 정규식 버전은 재귀 적으로 패턴 과 일치 할 수 있지만 * (그리고이를 위해 JSON과 일치시킬 수 있음) 결과 패턴은보기에 끔찍하며 프로덕션 코드 IMO에서 절대 사용해서는 안됩니다!
* 그러나 많은 정규식 구현은 재귀 패턴을 지원 하지 않습니다 . 널리 사용되는 프로그래밍 언어 중 Perl, .NET, PHP 및 Ruby 1.9.2와 같은 재귀 패턴을 지원합니다.
@mario의 대답을 시도했지만 JSON.org ( archive ) 에서 테스트 스위트를 다운로드 했고 4 개의 실패한 테스트 (fail1.json, fail18.json, fail25.json, fail27) 가 있었기 때문에 작동하지 않았습니다 . json).
나는 오류를 조사하고, 발견 한 fail1.json
(수동의에 따라 실제로 올 노트 와 RFC-7159 유효한 문자열도 유효한 JSON)입니다. 파일도 사실 fail18.json
이 아니 었습니다. 실제로 깊이 중첩 된 JSON이 실제로 포함되어 있기 때문입니다.
[[[[[[[[[[[[[[[[[[[["Too deep"]]]]]]]]]]]]]]]]]]]]
그래서 두 개의 파일 왼쪽 : fail25.json
및 fail27.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의 모든 법적 테스트를 통과 할 수 있습니다.
JSON 에 대한 문서를 살펴보면 목표가 적합성을 확인하는 것이라면 정규식이 단순히 세 부분이 될 수 있습니다.
[]
{}
[{\[]{1}
...[}\]]{1}
[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]
...""
".*?"
...모두 함께:
[{\[]{1}([,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]|".*?")+[}\]]{1}
json으로 문자열이 포함되어 있으면 newline
문자를, 당신은 사용해야 singleline
있도록 정규식 맛에 스위치를 .
일치 newline
. 모든 잘못된 JSON에서 실패하지는 않지만 기본 JSON 구조가 유효하지 않으면 실패합니다. 이는 파서에 전달하기 전에 기본 온 전성 유효성 검사를 수행하는 간단한 방법입니다.
작동하는 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
"문자열과 숫자"의 경우 숫자에 대한 부분 정규식이 다음과 같다고 생각합니다.
-?(?:0|[1-9]\d*)(?:\.\d+)(?:[eE][+-]\d+)?
대신 다음과 같아야합니다.
-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+\-]?\d+)?
숫자의 소수 부분은 선택 사항이며 대괄호 사이에 특별한 의미가 있으므로 -
기호 를 이스케이프하는 것이 더 안전 할 수 [+-]
있습니다.
\d
은 위험합니다. 많은 정규 표현식 구현 \d
에서 숫자의 유니 코드 정의와 일치 할 [0-9]
뿐만 아니라 대체 스크립트를 포함합니다.
JSON 배열의 후행 쉼표로 인해 Perl 5.16이 중단되었습니다. 역 추적 종료 지시문을 추가해야했습니다.
(?<json> \s* (?: (?&number) | (?&boolean) | (?&string) | (?&array) | (?&object) )(*PRUNE) \s* )
^^^^^^^^
이렇게하면 '선택적'( *
또는 ?
) 이 아닌 구조가 식별되면 다른 것으로 식별하기 위해 역 추적을 시도해서는 안됩니다.
위에서 설명한대로 사용하는 언어에 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)
무심코 이것을 깨뜨릴 수있는 것을 놓친 경우 댓글 감사합니다!
key (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)*\}$
{
"key":"string",
"key": 56,
"key":{
"attr":"integer",
"attr": 12
},
"key":{
"key":[
{
"attr": 4,
"attr": "string"
}
]
}
}
여기에 문자열 유효성 검사에 대한 정규식이 있습니다.
^\"([^\"\\]*|\\(["\\\/bfnrt]{1}|u[a-f0-9]{4}))*\"$
usign 기록 된 원래의 구문 Diagramm 사용을 .