Библиотека отображения объектов из JSON в NSObjects - PullRequest
13 голосов
/ 26 мая 2011

Я пытаюсь создать синтаксический анализатор / objectMapper, который будет создавать объекты Objective C для JSON, который я потребляю из службы REST.

Я черпал вдохновение из RestKit, так как все мои сущности содержат «список декодирования»msgstr "который сообщает мапперу, какие ключи JSON идут с какими объектами.Например:

//ObjectEntity implementation
+ (NSDictionary*) mapProperties {

    /*
     localPropertiy - JSONProperty
     */

    return @{
            @"name": @"name", 
            @"category": @"category", 
            @"possible_scopes": @"possibleScopes",
            @"possible_descriptions": @"possibleDescriptions",
            @"key": @"keys"            
     };

}

+ (NSDictionary*) mapRelations {

    return [NSDictionary dictionary];
}

Я сделал это, потому что мне нравится, когда инкапсуляция этих изменяемых значений находится в объекте, на который они ссылаются.Сделать так, чтобы Mapper знал как можно меньше.

Mapper делает что-то вроде этого:

+ (NSArray*) parseData:(NSData*) jsonData intoObjectsOfType:(Class) objectClass {

    //Parser result from web service
    NSError *error = nil;
    CJSONDeserializer *deserializer = [CJSONDeserializer deserializer];
    [deserializer setNullObject:nil];
    NSArray *objects = [deserializer deserializeAsArray:jsonData error:&error];

    NSMutableArray *result = [NSMutableArray array];

    for (NSDictionary *o in objects) {

        id <EntityProtocol> entity = [[objectClass alloc] init];

        NSDictionary *jsonKeys = objectClass.mapProperties;

        for (NSString *key in jsonKeys.allKeys) {

            NSString *objectProperty = jsonKeys[key];
            NSString *value = o[key];
            if (value)
                [entity setValue:value forKey:objectProperty];
        }

        [result addObject:entity];

    }

    return (NSArray*)result;
}

Итак, я отправляю парсеру / преобразователю следующее:

NSArray *objects = [ObjectParser parseData:self.responseData intoObjectsOfType:ObjectEntity.class];

Это означает, что синтаксический анализатор должен знать, что такое мой корневой объект, и это хорошо, поскольку объект, извлекающий его из веб-службы, конечно, обладает такими знаниями.

Вышеприведенное работает только для JSON без вложенных объектов, я былпытаясь построить синтаксический анализатор так, чтобы он также учитывал отношения, создавая необходимые объекты и вставляя их в корневой объект, это должно быть рекурсивно, и я продолжаю сталкиваться с тупиками.

Я бы хотелкак некоторая помощь в том, как я мог бы подойти к этому или к любому пониманию, как будто что-то подобное существует в виде библиотеки.Может быть, для использования или просто для решения частей, с которыми у меня есть проблемы.

Заранее спасибо.

Ответы [ 5 ]

10 голосов
/ 25 января 2012

Рассмотрите возможность использования RestKit: http://restkit.org

В этом фреймворке есть все, что вам нужно - абстракции REST и JSON, сопоставление объектов, даже поддержка Core Data и множество действительно полезных вещей, все реализовано настраиваемым и элегантным способом.

ОБНОВЛЕНИЕ: Хорошо, когда я писал еще один метод отображения, я решил, что больше не могу этого делать, и создал небольшой фреймворк. Он анализирует свойства объекта и с небольшой настройкой дает вам бесплатное красивое описание, isEqual / hashCode, бесплатную поддержку NSCoding и позволяет генерировать в / из сопоставителей JSON (ну, на самом деле, NSDictionary, но кто будет использовать его для чего-то другого). Все проверки NSNull, пропущенные поля в JSON, новые неожиданные поля в JSON обрабатываются корректно и сообщаются правильно.

Если кто-то хочет, чтобы эта публикация была общедоступной, вы можете дать мне несколько голосов или комментариев. Я бы сделал это в конце концов, но я мог бы поделиться этим быстрее.

2 голосов
/ 30 мая 2011

Почему бы не добавить сопоставления для классов?

+ (NSDictionary *)mapClasses {
    return @{
            @"category": [Category class],
            // ...
    };
}

Для неконтейнерных свойств вы можете даже сделать самоанализ свойств во время выполнения , чтобы избежать избыточных отображений.

Свойства контейнера могут отображаться на специальные объекты-оболочки:

[OMContainer arrayWithClass:Key.class], @"keys",
[OMContainer dicionaryWithKeyClass:ScopeID.class valueClass:Scope.class], @"possibleScopes",

Или даже блоки для динамического выбора типов:

[OMDynamicType typeWithBlock:^(id obj){
    if ([obj isKindOfClass:NSString.class] && [obj hasPrefix:@"foo"])
        return Foo.class;
    else
        return Bar.class;
}], @"foo", 

Реализация этого может выглядеть примерно так:

+ (NSArray *)parseData:(NSData*)jsonData intoObjectsOfType:(Class)objectClass {
    NSArray *parsed = /* ... */
    NSMutableArray *decoded = [NSMutableArray array];
    for (id obj in parsed) {
        [decoded addObject:[self decodeRawObject:parsed intoObjectOfType:objectClass]];
    }
    return decoded;
}

+ (id)decodeRawObject:(NSDictionary *)dict intoObjectOfType:(Class)objectClass {
    // ...
    NSDictionary *jsonKeys = objectClass.mapProperties;
    NSDictionary *jsonClasses = objectClass.mapClasses;

    for (NSString *key in jsonKeys.allKeys) {
        NSString *objectProperty = jsonKeys[key];
        NSString *value = dict[key];
        if (value) {
            id klass = jsonClasses[key];
            if (!klass) {
                [entity setValue:value forKey:objectProperty];
            } else if (klass == klass.class) {
                [entity setValue:[self decodeRawObject:value intoObjectOfType:klass]
                          forKey:objectProperty];
            } else if (/* check for containers and blocks */) {
                // ...
            }
        }
    }
    // ...
}
1 голос
/ 05 октября 2016

Я тоже попробовал такой же подход.Моей главной проблемой было точное описание типов.Например, для вложенных массивов я использовал что-то вроде этого:

@{ @"friends" : @[[Person class]] }

Но все стало грязно, потому что мне нужно было придумывать все больше и больше специальных синтаксисов для разных типов, которые я хотел поддерживать.В конце концов я перестал следовать этому подходу и пошел к другому решению - также потому, что проверки во время выполнения, которые мне нужно было сделать, замедляли преобразование.

В настоящее время существует множество решений.Я бы посоветовал взглянуть на потрясающий сайт iOS .И я также хотел бы отметить JSON Class Generator , потому что он позволяет

  • точно описывать ваши типы (включая специальные сопоставители для даты и т. Д.),
  • типы проверяются автоматически (сообщается о несоответствиях и предусмотрены запасные значения),
  • вам не нужно тратить время на написание повторяющегося кода (а сгенерированный код, кажется, всегда "просто работает")

Я ненавижу рекламировать, но этот инструмент избавил меня от большой работы, которую я вложил в другие функции своих приложений.Это вышло немного поздно, теперь, когда мир, похоже, переключился на Swift, но это еще одна причина не тратить время на написание моделей в Objective-C.

Вот несколько примеров кода, преобразующих в / форму JSON:

// converting MyClass <-> NSDictionary
MyClass       *myClass = [MyClass myClassWithDict:someJsonDictionary];
NSDictionary *jsonDict = [myClass toDict];

// converting NSDictionary <-> NSData
NSDictionary *someJsonDictionary = [NSDictionary api_dictionaryWithJson:someJsonData];
NSData *jsonData = [someJsonDictionary api_toJson];

Функции, упомянутые в предыдущем комментарии, также поддерживаются JSON Class Generator :

-isEqual, -hash, -description, -copy, NSCoding/NSSecureCoding/NSKeyedArchiver

бесплатно и проверяет отсутствие / дополнительные /NSNull / необязательные поля и неправильные типы в ответе JSON, сообщает об ошибках, изящно терпит неудачу со значениями возврата и делает это, используя диспетчеризацию метода, а не самоанализ типа времени выполнения (что быстрее).

0 голосов
/ 19 мая 2014

Моя рекомендация - использовать категорию Motis на NSObject. Он легкий, выполняет автоматическую проверку типов ваших объектов и очень прост в использовании.

В этом другом вопросе есть пример: Отображение объектов JSON в пользовательских объектах

Надеюсь, это полезно.

0 голосов
/ 29 января 2014

Попробуйте CSMapper, это потрясающе!Вы просто создаете списки имен с тем же именем, что и класс, сопоставляете некоторые свойства, затем вы можете легко сопоставить словарь с вашими объектами с помощью одной строки кода.Я протестировал его на многих проектах и ​​нашел его очень чистым и эффективным.Это дало мне гибкость, чтобы с легкостью реагировать на изменения API во время жизненного цикла разработки.

https://github.com/marcammann/CSMapper

В настоящее время я обновляю документацию и добавляю в проект личный форк, который, надеюсь, долженскоро будет объединено:)

https://github.com/AntonTheDev/CSMapper

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...