programing

JSON을 검증하기 위한 정규식

starjava 2023. 3. 8. 20:32
반응형

JSON을 검증하기 위한 정규식

json을 검증할 수 있는 Regex를 찾고 있습니다.

저는 Regex에 매우 익숙하지 않기 때문에 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 검증 간소화

보다 심플한 접근방식은 RFC4627 섹션6에서 규정되어 있는 최소한의 일관성 체크입니다.다만, 이것은 시큐러티 테스트와 기본적인 무효 예방책의 목적으로만 행해지고 있습니다.

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

예, 정규 표현식은 정규 언어만 일치시킬 수 있다는 것은 일반적인 오해입니다.실제로 PCRE 함수는 일반 언어보다 훨씬많이 일치할 수 있으며, 컨텍스트가 없는 일부 언어에도 일치할 수 있습니다.RegExps에 대한 위키피디아의 글은 그것에 대한 특별한 섹션을 가지고 있다.

JSON은 PCRE를 사용하여 여러 가지 방법으로 인식할 수 있습니다!@syslog는 이름 있는 서브패턴과 백레퍼런스를 사용한1개의 훌륭한 솔루션을 나타내고 있습니다.그리고 그는 재귀적 패턴을 이용한 해결책이 있어야 한다고 언급했다. (?R)PHP의 regexp에 관한 것입니다.

$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에 대한 )( )는 「」로 시작하지 않습니다.?: 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()(@Epcylon에서 설명한 것과 동일)그 JSON을 사용하는 경우(검증된 경우), 이것이 최선의 방법입니다.

JSON(네스트된 JSON)의 {...}-s) regex는 검증에 적합하지 않습니다.물론 일부 regex 플레이버는 반복적으로 패턴과* 일치할 수 있지만(또한 JSON과 일치할 수 있음) 결과 패턴은 보기에도 끔찍하며 프로덕션 코드 IMO에 절대 사용되지 않습니다.

* 단, 많은 regex 구현은 재귀 패턴을 지원하지 않습니다.일반적으로 사용되는 프로그래밍 언어 중 Perl , 는 재귀 패턴을 지원합니다.NET, PHP 및 Ruby 1.9.2

@fails의 답변을 시도했지만, JSON.org에서 테스트 스위트를 다운로드(fail1.json, fail18.json, fail25.json, fail27.json)한 4개의 테스트에 실패했기 때문에 잘 되지 않았습니다.

를 해 보니, ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★.fail1.json는 실제로 올바른 것입니다(매뉴얼의 주기에 따라 RFC-7159의 유효한 문자열도 유효한 JSON입니다).파일fail18.json을 가진 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의 문서를 보면 적합성을 확인하는 것이 목적이라면 regex는 단순히 세 부분으로 나눌 수 있습니다.

  1. 문자열은 다음 중 하나로 시작하고 끝납니다.[] ★★★★★★★★★★★★★★★★★」{}
  • [{\[]{1}...[}\]]{1}
  1. 그리고.
  2. 문자는 허용된 JSON 제어 문자입니다(1개만).
  • ........[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]
  1. 또는 에 포함된 문자 세트""
  • ........".*?"

함께 : ★★★★★★★★★★★★★★★★★★:[{\[]{1}([,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]|".*?")+[}\]]{1}

에 JSON이 포함되어 있는 newline후, 「 」, 「 」를 선택합니다.singleline regex가 "regex" 로 되도록 ..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+)?

이며, 이 숫자에서 탈출하는 이 더 할 수 .-symbol symbol in in의 [+-]

JSON 어레이의 후행 콤마로 인해 Perl 5.16이 중단되었습니다.아마도 계속 역추적이 이루어졌기 때문입니다.백트랙 종료 지시문을 추가해야 했습니다.

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

해서 '아닌 ('선택사항')이* ★★★★★★★★★★★★★★★★★」?다른 것으로 식별하기 위해 역추적을 시도해서는 안 됩니다.


JSONArray가 아닌 단순 JSON을 검증하는 정규식

키(문자열): value(string, value, [{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"
            }
        ]
     }
}

위에서 기술한 바와 같이 사용하는 언어에 JSON 라이브러리가 포함되어 있는 경우 문자열 디코딩을 시도하고 실패 시 예외/오류를 파악합니다.언어가 그렇지 않은 경우(FreeMarker와 같은 경우만 해당) 다음 regex는 최소한 매우 기본적인 검증을 제공할 수 있습니다(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)

제가 실수로 이걸 놓쳤다면댓글을 달아주셔서 감사합니다

여기서 문자열의 유효성을 확인하기 위한 regexp:

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

원본 구문 다이어그램으로 작성되었습니다.

이게 6년 전 일이라는 걸 알아요.하지만, 여기 있는 누구도 언급하지 않은 해결책이 있습니다. 이 솔루션은 리게이징보다 훨씬 쉽습니다.

function isAJSON(string) {
    try {
        JSON.parse(string)  
    } catch(e) {
        if(e instanceof SyntaxError) return false;
    };  
    return true;
}

언급URL : https://stackoverflow.com/questions/2583472/regex-to-validate-json

반응형