Простой JSON Schema валидатор для Objective-C

    Или сказ о том, как разработка JSON валидатора превратилась в очередной JSON binding


    Пока нормальные разработчики пишут приложения я изобретаю велосипеды.

    Наверное многие разработчики сталкивались с ситуацией, когда мобильное приложение разрабатывается параллельно с бэкэндом. При этом частенько структуры данных, приходящие в ответ на запрос с сервера, могут меняться. Например, на стороне бэкэнда решат поменять именование одного из ключиков JSON-а, забыв предупредить о этом мобильную команду. Я уже не говорю про ситуации, когда CamelCase нотацию «внезапно» решили поменять на underscore или наоборот. Вы можете сказать, что налицо плохая организация процесса и недостаток общения команд, и будете абсолютно правы. Но когда приложение на смартфоне заказчика перестает работать на демо все взгляды устремляются на мобильщиков.


    Одним из выходов из положения является валидация присланного JSON'а в соответствии с JSON-схемой (обзорная статья о JSON схемах).

    Например, если присланный нам JSON
    {
        "numberKey" : 100500,
        "arrayKey" : [
        {
            "number" : 1 ,
            "string" : "1"
        },
        {
            "number" : 2 ,
            "string" : "2"
        },
        {
            "number" : 3 ,
            "string" : "3"
        }
        ]
    }
    

    Минимальной JSON схемой, его описывающей будет
    {
        "type" : "object",
        "properties" : {
            "numberKey" : {
                "type" : "number"
            },
            "arrayKey" : {
                "type" : "array",
                "items" : {
                    "type" : "object",
                    "properties" : {
                        "number" : {
                            "type" : "number"
                        },
                        "string" : {
                            "type" : "string"
                        }
                    }
                }
            }
        }
    }
    

    Все, что нам в идеале нужно, это взять присланный JSON и провалидировать его с помощью схемы. Казалось-бы, проблема решена. JSON-schema — это стандартный способ описания JSON'а и валидаторы есть под многие языки программирования. Но поиски валидатора для языка Objective-C никакого вменяемого результата не дали, поэтому было решено пойти по пути велосипедостроения.

    В итоге на свет появилось поделие под названием JsonSchemaValidator (bitbucket). Для тех, кто захочет попробовать:
    pod 'SVJsonSchemaValidator'
    

    Так, с помощью этой библиотечки мы можем составить схему:
    id schema = [[SVType object] properties:@{
                           @"numberKey":[SVType number],
                           @"arrayKey":[[SVType array] items:[[SVType object] properties:@{
                                                                                    @"number" : [SVType number],
                                                                                    @"string" : [SVType string]
                                                                                }]]
                 }];

    Как видите, схема почти один к одному повторяет сам JSON. Теперь провалидируем его:
    NSError* error = nil;
    id validatedJson = [schema validateJson:json error:&error];
    Здесь переменные:
    json — это распаршенный любым парсером JSON (например, с помощью NSJSONSerialization).
    validatedJson — только те объекты, которые прошли валидацию.
    error — более или менее вменяемое описание того, какие объекты валидацию не прошли. nil — если все OK.

    Предположим, что у вас уже есть класс модели
    @interface MyModelObject : NSObject
     
    @property (strong, nonatomic) NSString* string;
    @property (strong, nonatomic) NSNumber* number;
     
    @end

    И делать двойную работу, и описывать в схеме уже существующие классы не хочется. Можно создать схему прямо из этого класса:
    id schema = [[SVType object] properties:@{
                           @"numberKey":[SVType number],
                           @"arrayKey":[[SVType array] items:[MyModelObject jsonSchema]]
                       }];

    С помощью objc-runtime метод -jsonSchema для класса MyModelObject пройдется по всем свойствам, которые можно представить в JSON и сгенерирует точно такую-же схему. С помощью этой же схемы можно инстанциировать обьекты типа MyModelObject и заполнить их свойства соответствующими значениями через KVC:
    NSDictionary* instanciated = [schema instantiateValidatedJson:validated];

    (lldb) po instanciated
    $1 = 0x0755fe70 {
        numberKey = 100500,
        arrayKey =     (
            "<MyModelObject: 0x7554a30>",
            "<MyModelObject: 0x7554610>",
            "<MyModelObject: 0x75543b0>",
            "<MyModelObject: 0x75619b0>"
        );
    }

    Вот и все, что реализовано на данный момент.
    А теперь вопрос, ради которого этот краткий топик и писался: нужен ли этот велосипед кому нибудь, а если да, то что от него еще требуется? Ответы жду в камментах.
    Поделиться публикацией
    Комментарии 6
      +1
      Валидация ответов сервера это отлично. Но что делать приложению на демо, если сервер прислал некорректный ответ?
        0
        Да все тоже самое, что и без валидации. Но когда после демо вы пойдете дебажить, чтобы узнать «какого черта», то тут появляется переменная error, которая содержит информацию о том, какие именно ключи json не соответствуют ожиданиям и почему. Это чуть проще, чем проверять вручную большой json.
        валидатор — это инструмент, и как каждый инструмент он имеет свою область применимости.
        0
        давно себе сделал подобный велосипед ) потому как нужен был мне
          0
          Если я правильно понял, то речь о JFFJsonValidator? Но ведь он просто валидирует на то, что все объекты являются объектами, представимыми в JSON.
          Основное предназначение валидатора схем — составление схемы (как ни странно) (структуры) JSON'а, что позволяет проверить, например, что по ключу «key_1» к нам прийдет именно массив, он будет содержать не больше 20 чисел со значением в диапазоне от 5 до 255(или 100500). А а по ключику «key_2» — строка, значение которой может быть только «A», «B» и «C» и т.д.
          Если я правильно из понял исходников, то JFFJsonValidator для такой задачи не предназначен.
          0
          Спасибо огромное! Пригодится.

          Добавьте плз свое творение в список реализаций на json-schema.org/implementations.html.

          Насколько я понял, объект схемы из JSON-описания создать нельзя, да?
            0
            Можно. Работает в обе стороны:

            [SVType schemaWithDictionary:jsonSchema];
            

            NSDictionary* jsonSchema = [NSString toJsonObject]
            

            Ну а превратить схему в NSDictionary вам поможет любой JSON парсер, например NSJSONSerialization

          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

          Самое читаемое