11 ответов:
да, возможна полная проверка регулярных выражений.
большинство современных реализаций регулярных выражений допускают рекурсивные выражения, которые могут проверить полную сериализованную структуру JSON. The 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 ';он работает довольно хорошо в PHP с функции PCRE . Должно работать без изменений в Perl, и, безусловно, может быть адаптирован для других языков. Также это удается с тест JSON дела.
более простая проверка RFC4627
более простой подход-это минимальная проверка целостности, как указано в RFC4627, раздел 6. Однако он просто предназначен для проверки безопасности и основной меры предосторожности против недействительности:
var my_JSON_object = !(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test( text.replace(/"(\.|[^"\])*"/g, ''))) && eval('(' + text + ')');
Да, это распространенное заблуждение, что регулярные выражения могут соответствовать только регулярные языки. На самом деле,функции PCRE могут соответствовать гораздо больше, чем обычные языки они могут сравниться даже некоторые не-контекстно-свободных языков! статья Википедии о регулярных выражениях есть специальный раздел об этом.
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)ссылки на регулярное выражение, отмеченное крайними круглыми скобками (вот почему самый внешний( )не начинается с?:). Таким образом, регулярное выражение становится длиной 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 с вызовом
json_decode()функция (так же, как @Epcylon отметил). Если я собираюсь использовать что JSON (если он проверен), то это лучший метод.
из-за рекурсивной природы JSON (вложенный
{...}- s), регулярное выражение не подходит для его проверки. Конечно, некоторые вкусы регулярных выражений могут рекурсивно соответствовать шаблонам* (и может поэтому соответствовать JSON), но полученные шаблоны ужасны для просмотра и никогда не должны использоваться в производственном коде IMO!* однако будьте осторожны, многие реализации регулярных выражений делают не поддержка рекурсивных моделей. Из популярных языков программирования, эти поддерживают рекурсивные шаблоны: Perl, .NET, PHP и Ruby 1.9.2
Я попытался ответить @mario, но это не сработало для меня, потому что я загрузил тестовый набор из JSON.org (архиве) и было 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"]оба содержат недопустимые символы. Поэтому я обновил шаблон следующим образом (string subpattern updated):
$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 может быть передан.
Я создал рубиновую реализацию решения Марио, которая действительно работает:
# 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+)?так как десятичная часть числа необязательна, а также, вероятно, безопаснее избегать
-символ[+-]так как он имеет особое значение между скобками
конечная запятая в массиве JSON заставила мой Perl 5.16 зависнуть, возможно, потому, что он продолжал отступать. Мне пришлось добавить директиву обратного хода:
(?<json> \s* (?: (?&number) | (?&boolean) | (?&string) | (?&array) | (?&object) )(*PRUNE) \s* ) ^^^^^^^^таким образом, как только он идентифицирует конструкцию, которая не является "необязательной" (
*или?), он не должен пытаться вернуться к нему, чтобы попытаться идентифицировать его как что-то еще.
глядя на документацию для JSON, кажется, что регулярное выражение может быть просто три части, если цель состоит только в том, чтобы проверить пригодность:
- строка начинается и завершается либо
[]или{}
[{\[]{1}...[}\]]{1}- и
- символ является допустимым управляющим символом JSON (просто один)
- ...
[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]...- или набор символов, содержащихся в
""
- ...
".*?"...все вместе:
[{\[]{1}([,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]|".*?")+[}\]]{1}если строка JSON содержит
newlineсимволов, то вы должны использоватьsinglelineвключите свой вкус регулярных выражений, чтобы.игрnewline. Обратите внимание, что это не будет сбоя на всех плохих JSON, но это произойдет, если базовая структура JSON недействительна, что является прямым способом сделать базовую проверку здравомыслия перед передачей ее синтаксическому анализатору.
Как было написано выше, если язык, который вы используете, имеет 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)Если я пропустил что-то, что сломало бы это непреднамеренно, я благодарен за комментарии!
вот мое регулярное выражение для проверки строки:
^\"([^\"\]*|\(["\\/bfnrt]{1}|u[a-f0-9]{4}))*\"$был написан usign оригинальный синтаксическая диаграмма.
Я понимаю, что это из более чем 6 лет назад. Тем не менее, я думаю, что есть решение, которое никто здесь не упоминал, что это намного проще, чем регулярное выражение
function isAJSON(string) { try { JSON.parse(string) } catch(e) { if(e instanceof SyntaxError) return false; }; return true; }
Comments