Pregunta

Estoy tratando de analizar un XML que estoy obteniendo de un servicio web con un NSXMLParserDelegate. los NSObject Quiero poner los datos (solo lo llamaré MyObject) posee @propertyS que no es NSStringS (hay algunos NSDateS, algunos NSNumberS, y tal vez en algún momento en el futuro algunas primitivas).

Dado que el XML es todo una gran cadena, estoy recibiendo errores y bloqueos cuando intento poner directamente los valores de la XML en mis propiedades (estoy usando [myObject setValue:currentElementValue forKey:elementName]; en mi parser:didEndElement:..., que normalmente funciona bien, cuando key es un NSString). Realmente no esperaba que esto funcionara, pero pensé que valía la pena.

Lo más fácil de hacer es tenerlo todo, así que si tengo un '@property (no atómico, fuerte) nsnumber *edad;' en mi objeto, cuando encuentro el <age>25</age> parte de mi XML, lo hago MyObject.age = [NSNumber numberWithInt:[currentElementValue intValue]];. El problema con esto es que es muy rígido: me gustaría que este código sea más dinámico que eso. No quiero tener que saber cuáles son todas las propiedades de mi objeto con anticipación.

También he intentado comprobar lo que el class de la propiedad es, luego hacer alguna conversión antes de ingresar un valor (por lo que algo como [myObject setValue:[NSNumber numberWithInt:[currentElementValue intValue] forKey:elementName]; Si la propiedad isKindOfClass:[NSNumber class]]. El problema con esto es que esta es la primera vez que se usa la propiedad, por lo que es nil, y por lo tanto no tiene clase. Así que este enfoque tampoco funciona realmente.

¿Estoy haciendo esto completamente mal? Parece que debería ser bastante típico obtener datos de un XML y ponerlos en noNSString Variables, pero parece que no puedo hacer que funcione.

Si alguien necesita ver más código para comprender mejor cómo estoy configurado o qué he probado, avíseme.


Editar: se me ocurrió un ejemplo rápido de lo que estoy tratando de hacer. Supongamos que tengo el siguiente XML:

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

que quiero analizar en una instancia de MyObject (un NSObject subclase)

@interface MyObject: NSObject

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

@end

Y un segundo XML

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

que entrará en una instancia de OtherObject (además NSObject subclase)

@interface OtherObject: NSObject

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

@end

Quiero crear un analizador de "plantilla" (que tiene @property (nonatomic, strong) NSObject *instance; y entonces NSMutableString *currentElementValue como un ivar)

...
- (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 ...
}
...

Entonces, solo subclase ese analizador de "plantilla" para cada uno de mis diferentes XML y sobrescribir el init método para que

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

o

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

según sea apropiado. Tengo muchos servicios web que estoy llamando que obtendré XML estructurado de manera similar, por lo que solo tener que cambiar 1 línea en los analizadores para cada uno es algo que es muy deseable. Tener todo el resto del código analizador en una sola clase de "plantilla" facilitará el mantenimiento, la lectura y la depuración del código.

Obviamente, el problema que tengo es isKindOfClass no acepta int, float, o BOOL como entradas. ¿Hay algo similar que pueda usar en su lugar?

¿Fue útil?

Solución

El problema con esto es que esta es la primera vez que se usa la propiedad, por lo que es nulo y, por lo tanto, no tiene una clase.

Objective-C permite Propiedades de la clase de consulta en tiempo de ejecución, por lo que introspectar el tipo de propiedad y usar una formación TRNS de apropiada es un camino a seguir.

Puedes leer más sobre el tiempo de ejecución en el blog de Mike Ash o ver Objetivo C Introspección/Reflexión.

Sin embargo, Este problema específico ha sido encontrado por muchas otras personas., entonces hay bastantes soluciones: como se dice en Biblioteca de servicios de descanso más maduros de iOS Restkit es un buen ejemplo de tal biblioteca; MagicRecord es otro.

Puede encontrar fácilmente un par de bibliotecas más para consultar antes de decidir implementar su propia solución.

Otros consejos

Por supuesto que podría anular el init método de MyObject para asignar todas mis propiedades a valores vacíos, lo que debería resolver el nil Problema que hice referencia en mi OP, pero eso se siente realmente hacky. Sin embargo, puede ser la única opción.


EDITAR: Este enfoque se desmorona totalmente con el ejemplo en la edición en el OP. KVC girará ints, floatarena BOOLes todo a NSNumberS, sin ninguna forma de saber qué están originalmente donde. Lo que significa que no podré llamar [currentElementValue intValue], [currentElementValue floatValue], o [currentElementValue boolValue] según sea apropiado.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top