Pergunta

Estou tentando analisar um XML que estou recebendo de um serviço da web com um NSXMLParserDelegate. o NSObject Eu quero colocar os dados (eu vou chamá -los MyObject) tem @propertys que não são NSStrings (existem alguns NSDates, alguns NSNumbers, e talvez em algum momento no futuro, alguns primitivos).

Como o XML é um grande string, estou recebendo erros e travamentos quando tento apenas colocar os valores diretamente do XML em minhas propriedades (estou usando [myObject setValue:currentElementValue forKey:elementName]; no meu parser:didEndElement:..., que normalmente funciona bem - quando key é um NSString). Eu realmente não esperava que isso funcionasse, mas achei que valia a pena tentar.

A coisa mais fácil de fazer é apenas codificar tudo, então, se eu tiver um '@property (não atômico, forte) nsnumber *idade;' no meu objeto, quando eu encontro o <age>25</age> parte do meu XML, eu faço MyObject.age = [NSNumber numberWithInt:[currentElementValue intValue]];. O problema é muito rígido - eu gostaria que esse código fosse mais dinâmico do que isso. Não quero saber quais todas as propriedades do meu objeto estão com antecedência.

Eu também tentei verificar o que o class da propriedade está, depois fazendo alguma conversão antes de entrar em um valor (então algo como [myObject setValue:[NSNumber numberWithInt:[currentElementValue intValue] forKey:elementName]; se a propriedade isKindOfClass:[NSNumber class]]. O problema disso é que é a primeira vez que a propriedade é usada, então é nil, e, portanto, não tem uma classe. Portanto, essa abordagem também não funciona.

Estou fazendo isso completamente errado? Parece que deve ser bastante típico obter dados de um XML e colocá-los em nãoNSString Variáveis, mas não consigo fazer com que funcione.

Se alguém precisar ver mais código para entender melhor como eu está configurado ou o que eu já tentei, me avise.


EDIT: Eu cheguei a um exemplo rápido do que estou tentando fazer. Suponha que eu tenha o seguinte XML:

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

que eu quero analisar uma instância de MyObject (um NSObject subclasse)

@interface MyObject: NSObject

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

@end

E um segundo xml

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

Isso entrará em um exemplo de OtherObject (também NSObject subclasse)

@interface OtherObject: NSObject

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

@end

Eu quero criar um analisador de "modelo" (que tem @property (nonatomic, strong) NSObject *instance; e depois NSMutableString *currentElementValue como um 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 ...
}
...

Então, apenas subclasse esse analisador de "modelo" para cada um dos meus diferentes xmls e substitui o init método para que

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

ou

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

como apropriado. Eu tenho muitos serviços da web que chamo de que vou receber XML de maneira semelhante, então apenas ter que alterar 1 linha nos analisadores para cada um é algo que é muito desejável. Ter todo o restante do código do analisador em uma única classe "modelo" facilitará muito a manutenção, a leitura e a depuração do código.

Obviamente, o problema que estou tendo é isKindOfClass não aceita int, float, ou BOOL como entradas. Existe algo semelhante que eu possa usar?

Foi útil?

Solução

O problema é que é a primeira vez que a propriedade é usada, por isso é nula e, portanto, não tem uma classe.

Objective-C permite Propriedades da classe de consulta em tempo de execução, por isso, introspectar o tipo de propriedade e usar uma transformação apropriada é um caminho a percorrer.

Você pode ler mais sobre o tempo de execução no blog de Mike Ash ou veja Objetivo.

No entanto, Este problema específico foi encontrado por muitas outras pessoas, então existem algumas soluções: como dito em Biblioteca de serviço de repouso mais madura do iOS Restkit é um bom exemplo dessa biblioteca; MagicRecord é outro.

Você pode encontrar facilmente mais algumas bibliotecas para verificar antes de decidir implementar sua própria solução.

Outras dicas

Claro que eu poderia apenas substituir o init método de MyObject para alocar todas as minhas propriedades para esvaziar valores, que devem resolver o nil Problema que referenciei no meu OP, mas isso parece realmente hacky. Pode ser a única opção.


EDIT: Essa abordagem se desfaz totalmente com o exemplo da edição no OP. KVC vai virar ints, floatareia BOOLé tudo para NSNumberS, sem nenhuma maneira de saber o que eles originalmente, onde. O que significa que não poderei ligar [currentElementValue intValue], [currentElementValue floatValue], ou [currentElementValue boolValue] como apropriado.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top