Регулярное выражение для проверки JSON



Я ищу регулярное выражение, которое позволяет мне проверить json.



Я очень новичок в регулярных выражениях, и я знаю достаточно, что разбор с регулярным выражением плох, но может ли он использоваться для проверки?

1945   11  

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}...[}\]]{1}
  2. и
    1. символ является допустимым управляющим символом JSON (просто один)
      • ...[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]...
    2. или набор символов, содержащихся в ""
      • ...".*?"...

все вместе: [{\[]{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

    Ничего не найдено.