2013-11-07 1 views
0

Я пытаюсь проанализировать XML, который я получаю из веб-службы с помощью NSXMLParserDelegate. NSObject Я хочу поместить данные (я просто позвоню MyObject) имеет @property s, которые не являются NSString s (есть некоторые NSDate s, некоторые NSNumber и, возможно, в какой-то момент в будущем некоторые примитивы).Динамический анализ XML в переменных, отличных от NSString

Поскольку XML - это большая строка, я получаю ошибки и сбои, когда пытаюсь просто прямо поместить значения из XML в свои свойства (я использую [myObject setValue:currentElementValue forKey:elementName]; в своем parser:didEndElement:..., который обычно работает нормально - когда key - NSString). Я действительно не ожидал, что это сработает, но я подумал, что это стоит того.

Проще всего сделать это всего лишь hardcode, поэтому, если у меня есть "@property (nonatomic, strong) NSNumber * age; ' на моем объекте, когда я нахожу <age>25</age> часть моего XML, я делаю MyObject.age = [NSNumber numberWithInt:[currentElementValue intValue]];. Проблема с этим в том, что она очень жесткая - я бы хотел, чтобы этот код был более динамичным. Я не хочу знать, что все свойства моего объекта опережают время.

Я также попытался проверить, что class в собственности, а затем делать некоторые преобразования, прежде чем ввести значение (так что-то вроде [myObject setValue:[NSNumber numberWithInt:[currentElementValue intValue] forKey:elementName];, если свойство isKindOfClass:[NSNumber class]]. Проблема в том, что это первый раз, когда свойство так что это nil, и поэтому у него нет класса. Поэтому такой подход тоже не работает.

Я делаю это совершенно неправильно? Кажется, должно быть довольно типично получать данные из XML и помещать его в переменные не NSString, но я не могу заставить его работать.

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


EDIT: Я придумал простой пример того, что я пытаюсь сделать.Предположим, у меня есть следующий XML:

<Object> 
    <ITEM_NUMBER>4</ITEM_NUMBER> 
    <IS_AWESOME>YES</IS_AWESOME> 
    <AWESOMENESS>9000.1</AWESOMENESS> 
</Object> 

, что я хочу, чтобы разобрать в экземпляр MyObject (NSObject подкласса)

@interface MyObject: NSObject 

@property (nonatomic, assign) int ITEM_NUMBER; 
@property (nonatomic, assign) BOOL IS_AWESOME; 
@property (nonatomic, assign) float AWESOMENESS; 

@end 

И второй XML

<OtherObject> 
    <PRICE>4.99</PRICE> 
    <QUANTITY>5</QUANTITY> 
    <FOR_RESALE>NO</FOR_RESALE> 
</OtherObject> 

, который будет идти в OtherObject (также NSObject)

@interface OtherObject: NSObject 

@property (nonatomic, assign) int QUANTITY; 
@property (nonatomic, assign) BOOL FOR_RESALE; 
@property (nonatomic, assign) float PRICE; 

@end 

Я хочу, чтобы создать «шаблон» синтаксический анализатор (который имеет @property (nonatomic, strong) NSObject *instance;, а затем NSMutableString *currentElementValue как Ивар)

... 
- (void) parser:(NSXMLParser *)parser foundCharacters:(NSString *)string 
{ 
    [currentElementValue setString:string]; 
} 

- (void) parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName 
{ 
    if ([[self.instance valueForKey:elementName] isKindOfClass:int]) 
    { 
     [self.instance setValue:[currentElementValue intValue] forKey:elementName]; 
    } 
    else if ([[self.instance valueForKey:elementName] isKindOfClass:float]) 
    { 
     [self.instance setValue:[currentElementValue floatValue] forKey:elementName]; 
    } 
    else if ([[self.instance valueForKey:elementName] isKindOfClass:BOOL]) 
    { 
     [self.instance setValue:[currentElementValue boolValue] forKey:elementName]; 
    } 
    else if ... 
} 
... 

Тогда я просто подкласс, что «шаблон» синтаксический анализатор для каждого из моих различных XMLs и перезаписать метод init так, что

self.instance = [[MyObject alloc] init]; 

или

self.instance = [[OtherObject alloc] init]; 

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

Очевидно, что проблема у меня есть isKindOfClass не принимает int, float или BOOL в качестве входов. Есть ли что-то подобное, что я могу использовать вместо этого?

+1

Общеизвестно, что делегат-парсер знает о свойствах ваших объектов. Нет ничего плохого в создании объекта NSNumber из значения XML и присвоении его объекту. Однако, если вы хотите сделать свой код немного более многоразовым, вы можете создать загрузчик для каждого из ваших классов моделей, чтобы избежать использования всего вашего кода в методе 'parser: didEndElement:'. Затем просто передайте свои атрибуты каждому загрузчику. –

+0

@CraigOtis: Если я создаю идею «загрузчика», которую я предлагаю, мне нужно будет по существу написать собственный настраиваемый инструмент для каждого свойства в моих объектах? Так что, действительно, мне все же приходится выполнять ту же работу, что и hardcoding в методе 'didEndElement:', она просто перемещается, где эта работа выполняется, правильно? – GeneralMike

+0

В любом случае, ваши свойства должны иметь сеттеры. Вы должны * не * полагаться на методы 'setValue: forKey:' для создания и настройки ваших объектов. –

ответ

1

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

Objective-C позволяет свойств класса во время выполнения запроса, поэтому интроспекция типа свойства и используя соответствующий trnsformation один путь.

Вы можете прочитать больше о времени выполнения at Mike Ash's blog или увидеть Objective C Introspection/Reflection.

Однако эта конкретная проблема была выявлена ​​многими другими людьми, так что есть довольно много решений: как сказано в iOS Most mature REST service library RESTKit является одним хорошим примером такой библиотеки; MagicRecord - это другое.

Вы можете легко найти еще несколько библиотек, чтобы проверить, прежде чем решите реализовать свое собственное решение.

+0

У меня уже есть большая часть синтаксического анализа и поддержки, поэтому я думаю, что сама интроспекция звучит так, как мне будет легче всего в данный момент. Я прочитал ссылки, которые вы предоставили, но я не видел ничего, что на самом деле описывало, как указать тип переменной для данного свойства, где я сейчас застрял. Вы знаете синтаксис для этого? – GeneralMike

+1

А, это требует некоторого щелчка. Это 'property_getAttributes (свойство)'. Посмотрите на https://developer.apple.com/library/mac/documentation/cocoa/conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html или играйте с https://github.com/mikeash/ MAObjCRuntime/blob/master/RTProperty.m –

+0

Просто примечание: вам нужно '#import ' использовать его! – GeneralMike

0

Конечно, я мог бы просто переопределить init метод MyObject к Alloc-инициализировать все мои свойства пустые значения, которые должны решить эту проблему, я nil ссылки в моем О.П., но это просто чувствует себя действительно Hacky. Однако это может быть единственный вариант.


EDIT: этот подход полностью разваливается с примером в EDIT в OP. KVC превратится в int s, float s, и BOOL s все до NSNumber s, без какого-либо знания того, из чего они изначально были. Это означает, что я не смогу позвонить по телефону [currentElementValue intValue], [currentElementValue floatValue], или [currentElementValue boolValue], если необходимо.