Pull to refresh

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

Reading time 5 min
Views 10K

Или сказ о том, как разработка 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>"
    );
}

Вот и все, что реализовано на данный момент.
А теперь вопрос, ради которого этот краткий топик и писался: нужен ли этот велосипед кому нибудь, а если да, то что от него еще требуется? Ответы жду в камментах.
Tags:
Hubs:
+8
Comments 6
Comments Comments 6

Articles