Зачем нужен отдельный этап проверки? В большинстве языков есть JSON-библиотеки, которые могут анализировать JSON, и если он может его анализировать, это было правильно. Если нет, библиотека сообщит вам.
Epcylon
Вам нужно проанализировать текст, чтобы проверить его ...
Кен
3
@mario - Я не знаю ... Я полностью за злоупотребление регулярным выражением и очень сочувствую вашему возражению против заблуждения "регулярное выражение должно соответствовать обычному" - но не по практическим вопросам, связанным с работой. Лучший ответ здесь - действительно комментарий Epcylon ... (может быть, это обсуждение относится к чату?)
Коби
1
Другой практический вариант использования - поиск выражений JSON в более крупной строке. Если вы просто хотите спросить, «является ли эта строка здесь объектом JSON?», Тогда да, библиотека синтаксического анализа JSON, вероятно, лучший инструмент. Но он не может найти для вас объекты JSON в более крупной структуре.
Да, возможна полная проверка регулярного выражения.
Большинство современных реализаций регулярных выражений допускают рекурсивные повторные выражения, которые могут проверять полную сериализованную структуру JSON. Спецификация json.org делает это довольно просто.
Он довольно хорошо работает в PHP с функциями PCRE . Должен работать в Perl без изменений; и, безусловно, может быть адаптирован для других языков. Также это удается с тестовыми примерами JSON .
Упрощенная проверка RFC4627
Более простой подход - минимальная проверка согласованности, как указано в RFC4627, раздел 6 . Однако он предназначен только для проверки безопасности и основной меры предосторожности, связанной с недействительностью:
var my_JSON_object = !(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test(
text.replace(/"(\\.|[^"\\])*"/g, ''))) &&
eval('(' + text + ')');
+1 В мире столько всего плохого от людей, которые просто не понимают синтаксис регулярных выражений и злоупотребляют этим как причиной их ненависти :(
NikiC 05
8
@mario, не уверен, что я из отдела скептиков , но это не так. Обратите внимание, что ваше утверждение «Большинство современных реализаций регулярных выражений допускают рекурсивные повторные выражения» является весьма спорным. AFAIK, только Perl, PHP и .NET имеют возможность определять рекурсивные шаблоны. Я бы не назвал это «самым».
Bart Kiers
3
@Bart: Да, это правильно спорно. По иронии судьбы, механизмы регулярных выражений Javascript не могут использовать такое рекурсивное регулярное выражение для проверки JSON (или только с помощью сложных обходных путей). Так что если regex == posix regex, это не вариант. Тем не менее интересно, что это выполнимо с помощью современных реализаций; даже с несколькими практическими вариантами использования. (Но правда, libpcre не является распространенным движком повсюду.) - Также для протокола: я надеялся на значок синтетического разворота, но то, что вы не получили несколько одобрительных голосов, мешает этому. : /
марио
4
Нет. Я был за значком «Популист», за который мне нужно 20 голосов, но все же 10 голосов за ваш ответ. Напротив, отрицательные голоса по вашему вопросу не в мою пользу.
Марио
2
Что ж, если заглянуть дальше, у этого регулярного выражения есть много других проблем. Он соответствует данным JSON, но некоторые данные, не относящиеся к JSON, также совпадают. Например, один литерал falseсоответствует, тогда как значение JSON верхнего уровня должно быть либо массивом, либо объектом. У него также есть много проблем с набором символов, разрешенным в строках или в пробелах.
дольмен
32
Да, это распространенное заблуждение, что регулярные выражения могут соответствовать только обычным языкам . Фактически, функции 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)ссылки на регулярное выражение, отмеченные крайними круглыми скобками (именно поэтому крайнее выражение ( )не начинается с ?:). Таким образом, RegExp становится длиной 268 символов :)
В любом случае, это следует рассматривать как «демонстрацию технологии», а не как практическое решение. В PHP я проверю строку JSON с помощью вызова json_decode()функции (как отмечалось в @Epcylon). Если я собираюсь использовать этот JSON (если он подтвержден), то это лучший метод.
Использование \dопасно. Во многих реализациях регулярных выражений \dсоответствует определению Unicode цифры, которое не просто, [0-9]а вместо этого включает альтернативные сценарии.
дольмен
@dolmen: возможно, вы правы, но вы не должны редактировать это самостоятельно в вопросе. Достаточно просто добавить его в качестве комментария.
Деннис Хаарбринк
Я думаю, \dчто не соответствует номерам Unicode в PHP-реализации PCRE. Например, ٩символ (0x669 арабско-индийская цифра девять) будет сопоставлен с использованием шаблона, #\p{Nd}#uно не будет#\d#u
Грант Хачатрян
@ Грант-Хачатрян: нет, потому что вы не использовали /uфлаг. JSON кодируется в UTF-8. Для правильного регулярного выражения вы должны использовать этот флаг.
дольмен
1
@dolmen Я использовал uмодификатор, пожалуйста, посмотрите еще раз на шаблоны из моего предыдущего комментария :) Строки, числа и логические значения правильно сопоставлены на верхнем уровне. Вы можете вставить длинное регулярное выражение сюда quanetic.com/Regex и попробовать себя
Грант Хачатрян
14
Из-за рекурсивного характера JSON (вложенные {...}-s) регулярное выражение не подходит для его проверки. Конечно, некоторые разновидности регулярных выражений могут рекурсивно соответствовать шаблонам * (и поэтому могут соответствовать JSON), но полученные шаблоны ужасны для просмотра и никогда не должны использоваться в производственном коде IMO!
* Однако будьте осторожны, многие реализации регулярных выражений не поддерживают рекурсивные шаблоны. Из популярных языков программирования они поддерживают рекурсивные шаблоны: Perl, .NET, PHP и Ruby 1.9.2.
@ всех избирателей: «регулярное выражение не подходит для его проверки» не означает, что определенные механизмы регулярных выражений не могут этого сделать (по крайней мере, это то, что я имел в виду). Конечно, некоторые реализации регулярных выражений могут , но любой в здравом уме просто использовал бы парсер JSON. Точно так же, как если кто-то спросит, как построить полноценный дом, используя только молоток, я отвечу, что молоток не подходит для этой работы, вам понадобится полный набор инструментов и оборудование. Конечно, кто-то с достаточной выносливостью может сделать это одним лишь молотком.
Bart Kiers
1
Это может быть правильным предупреждением, но оно не отвечает на вопрос . Регулярное выражение может быть неправильным инструментом, но у некоторых людей нет выбора. Мы привязаны к продукту поставщика, который оценивает выходные данные службы для проверки ее работоспособности, и единственный вариант, который поставщик предоставляет для настраиваемой проверки работоспособности, - это веб-форма, которая принимает регулярное выражение. Продукт поставщика, который оценивает статус обслуживания, не находится под контролем моей команды. Для нас оценка JSON с помощью регулярного выражения теперь является требованием, поэтому ответ «неподходящий» не является жизнеспособным. (Я все еще не голосовал против вас.)
Джон Детерс
12
Я попробовал ответить @ mario, но у меня это не сработало, потому что я загрузил набор тестов с JSON.org ( архив ), и было 4 неудачных теста (fail1.json, fail18.json, fail25.json, fail27. json).
Я исследовал ошибки и выяснил, что fail1.jsonэто действительно правильно (согласно примечанию к руководству, и действительная строка RFC-7159 также является действительным JSON). Файл fail18.jsonтоже был не тот, потому что он действительно содержит правильный глубоко вложенный JSON:
Это также будет соответствовать только значениям JSON (строки, логические значения и числа), которые не являются объектом / массивом JSON.
kowsikbabu
4
Глядя на документацию для 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 недействительна, что является прямым способом выполнить базовую проверку работоспособности перед передачей ее в синтаксический анализатор.
Предлагаемое регулярное выражение имеет ужасное поведение при возврате в некоторых тестовых случаях. Если вы попробуете запустить его на '{"a": false, "b": true, "c": 100, "', этот неполный json, он остановится. Пример: regex101.com/r/Zzc6sz . Простое исправление: : [{[] {1} ([,: {} [] 0-9. \ - + Eaeflnr-u \ n \ r \ t] | ". *?") + [}]] {1}
Toonijn
@Toonijn Я обновил, чтобы отразить ваш комментарий. Благодаря!
cjbarth 03
Эта слегка измененная версия @cjbarth идеально подходит для моего случая использования поиска всех JSON-подобных структур в тексте (глобально применяется к HTML-файлу в моем случае):[{\[]{1}([,:{}\[\]0-9.\-+A-zr-u \n\r\t]|".*:?")+[}\]]{1}
C2BB
3
Я создал Ruby-реализацию решения Марио, которая действительно работает:
# encoding: utf-8moduleConstantsJSON_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
classString
def unindent
gsub(/^#{scan(/^(?!\n)\s*/).min_by{|l|l.length}}/u, "")
endend
require 'test/unit' unless defined? Test::UnitclassJsonValidationTest < Test::Unit::TestCaseincludeConstants
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)
endendend
Использование \ d опасно. Во многих реализациях регулярных выражений \ d соответствует определению Unicode цифры, которое не просто [0-9], но вместо этого включает альтернативные сценарии. Поэтому, если поддержка Unicode в Ruby по-прежнему не работает, вам нужно исправить регулярное выражение в своем коде.
дольмен
Насколько мне известно, Ruby использует PCRE, в котором \ d не соответствует ВСЕМ определениям Unicode для "digit". Или вы говорите, что надо?
pmarreck
За исключением того, что это не так. Ложно-положительный результат: «\ x00», [Верно]. Ложноотрицательный: «\ u0000», «\ n». Зависает: "[{" ": [{" ": [{" ":" (повторяется 1000 раз).
nst
Нетрудно добавить в качестве тестовых примеров, а затем настроить код для прохождения. Как заставить его не взорвать стек с глубиной 1000+ - это совсем другое дело ...
pmarreck 08
1
Что касается «строк и чисел», я думаю, что частичное регулярное выражение для чисел:
-?(?:0|[1-9]\d*)(?:\.\d+)(?:[eE][+-]\d+)?
вместо этого должно быть:
-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+\-]?\d+)?
поскольку десятичная часть числа является необязательной, и, вероятно, безопаснее избегать -символа в, [+-]поскольку он имеет особое значение между скобками
Использование \dопасно. Во многих реализациях регулярных выражений \dсоответствует определению Unicode цифры, которое не просто, [0-9]а вместо этого включает альтернативные сценарии.
дольмен
Выглядит немного странно, что -0 - допустимое число, но RFC 4627 разрешает это, и ваше регулярное выражение ему соответствует.
откр
1
Завершающая запятая в массиве JSON приводила к зависанию моего Perl 5.16, возможно, потому, что он продолжал возвращаться. Мне пришлось добавить директиву, завершающую возврат:
Таким образом, как только он идентифицирует конструкцию, которая не является «необязательной» ( *или ?), ему не следует пытаться выполнить обратное отслеживание, чтобы попытаться идентифицировать ее как что-то еще.
Как было написано выше, если используемый вами язык имеет прилагаемую к нему 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)
если я пропустил что-то, что могло бы случайно сломать это, я благодарен за комментарии!
Я понимаю, что это было более 6 лет назад. Однако я думаю, что есть решение, о котором здесь никто не упоминал, которое намного проще, чем регулярное выражение
Ответы:
Да, возможна полная проверка регулярного выражения.
Большинство современных реализаций регулярных выражений допускают рекурсивные повторные выражения, которые могут проверять полную сериализованную структуру 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 ';
Он довольно хорошо работает в PHP с функциями PCRE . Должен работать в Perl без изменений; и, безусловно, может быть адаптирован для других языков. Также это удается с тестовыми примерами JSON .
Упрощенная проверка RFC4627
Более простой подход - минимальная проверка согласованности, как указано в RFC4627, раздел 6 . Однако он предназначен только для проверки безопасности и основной меры предосторожности, связанной с недействительностью:
var my_JSON_object = !(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test( text.replace(/"(\\.|[^"\\])*"/g, ''))) && eval('(' + text + ')');
источник
false
соответствует, тогда как значение JSON верхнего уровня должно быть либо массивом, либо объектом. У него также есть много проблем с набором символов, разрешенным в строках или в пробелах.Да, это распространенное заблуждение, что регулярные выражения могут соответствовать только обычным языкам . Фактически, функции 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)
ссылки на регулярное выражение, отмеченные крайними круглыми скобками (именно поэтому крайнее выражение( )
не начинается с?:
). Таким образом, 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 с помощью вызова
json_decode()
функции (как отмечалось в @Epcylon). Если я собираюсь использовать этот JSON (если он подтвержден), то это лучший метод.источник
\d
опасно. Во многих реализациях регулярных выражений\d
соответствует определению Unicode цифры, которое не просто,[0-9]
а вместо этого включает альтернативные сценарии.\d
что не соответствует номерам Unicode в 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 ( архив ), и было 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 недействительна, что является прямым способом выполнить базовую проверку работоспособности перед передачей ее в синтаксический анализатор.источник
[{\[]{1}([,:{}\[\]0-9.\-+A-zr-u \n\r\t]|".*:?")+[}\]]{1}
Я создал 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
соответствует определению Unicode цифры, которое не просто,[0-9]
а вместо этого включает альтернативные сценарии.Завершающая запятая в массиве JSON приводила к зависанию моего Perl 5.16, возможно, потому, что он продолжал возвращаться. Мне пришлось добавить директиву, завершающую возврат:
(?<json> \s* (?: (?&number) | (?&boolean) | (?&string) | (?&array) | (?&object) )(*PRUNE) \s* ) ^^^^^^^^
Таким образом, как только он идентифицирует конструкцию, которая не является «необязательной» (
*
или?
), ему не следует пытаться выполнить обратное отслеживание, чтобы попытаться идентифицировать ее как что-то еще.источник
Как было написано выше, если используемый вами язык имеет прилагаемую к нему JSON-библиотеку, используйте ее, чтобы попытаться декодировать строку и поймать исключение / ошибку в случае сбоя! Если языка нет (как раз был такой случай с FreeMarker), следующее регулярное выражение могло бы, по крайней мере, обеспечить некоторую базовую проверку (он написан для PHP / PCRE, чтобы его можно было тестировать / использовать для большего количества пользователей). Это не так надежно, как принятое решение, но и не так страшно =):
краткое объяснение:
// 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)
если я пропустил что-то, что могло бы случайно сломать это, я благодарен за комментарии!
источник
Регулярное выражение, которое проверяет простой JSON, а не JSONArray
он проверяет ключ (строка): значение (строка, целое число, [{ключ: значение}, {ключ: значение}], {ключ: значение})
^\{(\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" } ] } }
источник
Вот мое регулярное выражение для проверки строки:
^\"([^\"\\]*|\\(["\\\/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; }
источник