Domanda

Sto cercando di analizzare un XML che sto ottenendo da un servizio web con un NSXMLParserDelegate. Il NSObject Voglio inserire i dati (li chiamerò e basta MyObject) ha @propertys che non lo sono NSStrings (ce ne sono alcuni NSDateS, alcuni NSNumbers, e forse ad un certo punto in futuro alcuni primitivi).

Dal momento che l'XML è tutto una grande stringa, ricevo errori e crash quando provo a mettere direttamente i valori dall'XML nelle mie proprietà (sto usando [myObject setValue:currentElementValue forKey:elementName]; nel mio parser:didEndElement:..., che normalmente funziona bene - quando key è un NSString). Non mi aspettavo davvero che funzionasse, ma ho pensato che valesse la pena.

La cosa più semplice da fare è solo il code tutto, quindi se ho un'età "@property (non atomica, forte) nsnumber *; sul mio oggetto, quando trovo il <age>25</age> parte del mio XML, lo faccio MyObject.age = [NSNumber numberWithInt:[currentElementValue intValue]];. Il problema è che è molto rigido: vorrei che questo codice fosse più dinamico di così. Non voglio sapere quali sono tutte le proprietà del mio oggetto in anticipo.

Ho anche provato a controllare cosa class della proprietà, quindi sta facendo qualche conversione prima di inserire un valore (quindi qualcosa di simile [myObject setValue:[NSNumber numberWithInt:[currentElementValue intValue] forKey:elementName]; Se la proprietà isKindOfClass:[NSNumber class]]. Il problema è che questa è la prima volta che viene utilizzata la proprietà, quindi lo è nil, e quindi non ha una classe. Quindi neanche questo approccio funziona.

Lo sto facendo completamente male? Sembra che dovrebbe essere abbastanza tipico ottenere dati da un XML e metterli in nonNSString Variabili, ma non riesco a farlo funzionare.

Se qualcuno ha bisogno di vedere più codice per comprendere meglio come sono impostato o cosa ho già provato, fammi sapere.


EDIT: ho escogitato un rapido esempio di ciò che sto cercando di fare. Supponiamo di avere il seguente XML:

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

che voglio analizzare in un'istanza di MyObject (un NSObject sottoclasse)

@interface MyObject: NSObject

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

@end

E un secondo XML

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

che andrà in un esempio di OtherObject (anche NSObject sottoclasse)

@interface OtherObject: NSObject

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

@end

Voglio creare un parser "modello" (che ha @property (nonatomic, strong) NSObject *instance; poi NSMutableString *currentElementValue come 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 ...
}
...

Quindi, sottoclasse quel parser "modello" per ciascuno dei miei diversi XML e sovrascrivo il init metodo così

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

o

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

Se appropriato. Ho molti servizi web che chiamo che riceverò XML strutturato in modo simile, quindi dover cambiare solo 1 linea nei parser per ciascuno è qualcosa di molto desiderabile. Avere tutto il resto del codice parser in una singola classe "modello" renderà molto più semplice la manutenzione, la lettura e il debug del codice.

Ovviamente, il problema che sto riscontrando è isKindOfClass non accetta int, float, o BOOL come input. C'è qualcosa di simile che posso usare invece?

È stato utile?

Soluzione

Il problema è che questa è la prima volta che viene utilizzata la proprietà, quindi è zero e quindi non ha una classe.

Obiettivo-C consente di Proprietà di classe di query in fase di esecuzione, quindi l'introspettivo del tipo di proprietà e l'utilizzo di una TRNSformazione appropriata è un modo per andare.

Puoi leggere di più su Runtime al blog di Mike Ash o vedere Obiettivo C Introspezione/Riflessione.

Tuttavia, Questo problema specifico è stato riscontrato da molte altre persone, quindi ci sono alcune soluzioni: come detto in libreria di servizio di riposo più matura di iOS Restkit è un buon esempio di tale biblioteca; MagicRecord è un altro.

Puoi facilmente trovare un paio di librerie in più da controllare prima di decidere di implementare la tua soluzione.

Altri suggerimenti

Ovviamente potrei semplicemente scavalcare init metodo di MyObject per alloc-init tutte le mie proprietà a valori vuoti, che dovrebbero risolvere il nil Problema a cui ho fatto riferimento nel mio OP, ma sembra davvero hacky. Potrebbe essere l'unica opzione però.


EDIT: questo approccio cade totalmente con l'esempio nella modifica dell'OP. KVC si girerà intS, floatsabbia BOOLè tutto a NSNumbers, senza alcun modo di sapere cosa inizialmente dove. Ciò significa che non potrò chiamare [currentElementValue intValue], [currentElementValue floatValue], o [currentElementValue boolValue] Se appropriato.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top