Подтвердить что ты не робот

Библиотека сопоставления объектов из JSON в NSObjects

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

Я немного вдохновил RestKit на то, что мои Entities все держат "список расшифровки", который сообщает картографу, с которым идут ключи 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 известно как можно меньше.

Преобразователь делает что-то вроде этого:

+ (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 без вложенных объектов, я пытаюсь создать парсер так, чтобы он учитывал отношения, а также создавал необходимые объекты и вставлял их в корневой объект, это должно быть рекурсивным и я продолжаю тупик.

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

Спасибо заранее.

4b9b3361

Ответ 1

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

+ (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 */) {
                // ...
            }
        }
    }
    // ...
}

Ответ 2

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

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

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

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

Ответ 3

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

@{ @"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, сообщает об ошибках, изящно терпит неудачу с запасными значениями и делает это с помощью диспетчеризации метода, а не самоанализа типа во время выполнения (что быстрее).

Ответ 4

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

https://github.com/marcammann/CSMapper

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

https://github.com/AntonTheDev/CSMapper