2011-01-02 5 views
1

J'ai un fichier XML, montré ci-dessous. J'utilise NSXMLParser, mais je ne suis pas capable d'analyser mon auteur et mon résumé. En raison des droits d'accès, je ne peux pas modifier le fichier XML.XML Parser (Objective C)

Une solution?

fichier XML:

<book> 
<title>Book 1</title> 
<author> 
<subfield id="a"> Jason </subfield> 
<subfield id="b"> Alfonso. </subfield> 
</author> 
<summary> 
<subfield id="a"> Milano </subfield> 
<subfield id="b"> Italy </subfield> 
</summary> 
</book> 

Mon code:

- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{ 
    currentElement = [elementName copy]; 
    attributes = [attributeDict copy]; 

    if ([elementName isEqualToString:@"book"]) { 
     item = [[NSMutableDictionary alloc] init]; 
    } else if ([elementName isEqualToString:@"title"]) { 
     self.title = [[NSMutableString alloc] init]; 
    } else if ([elementName isEqualToString:@"subfield"]) { 
     if ([[attributeDict valueForKey:@"id"] isEqualToString:@"a"]) { 
      self.authorName1 = [[NSMutableString alloc] init]; 
     } 
    } else if ([elementName isEqualToString:@"subfield"]) { 
     if ([[attributeDict valueForKey:@"id"] isEqualToString:@"b"]) { 
         self.authorName2 = [[NSMutableString alloc] init]; 
     } 
    } 
} 

je suis en mesure de saisir le sous-champ. Cependant même sous-champ de résumé est saisir pendant l'auteur que je ne sais pas quoi. J'ai besoin des deux pour être séparés

+2

Afficher le code que vous avez essayé. Voir ["Gestion des éléments et attributs XML"] (http://developer.apple.com/library/ios/ipad/#documentation/cocoa/Conceptual/XMLParsing/Articles/HandlingElements.html) dans les documents Apple pour une bonne explication et un exemple de code. – Anna

+1

au moins afficher du code. Apple a plusieurs exemples pour traiter avec XML, qui avez-vous revue? –

+1

Pour en savoir plus sur les questions sur SO, lisez ["Rédaction de la question parfaite"] (http://tinyurl.com/so-hints) et ["Le court, autonome, correct (compilable), exemple"] (http : // sscce.org /). – outis

Répondre

4

Notez qu'il pourrait être plus facile de baser votre application sur Core Data ou NSXML (en utilisant un init method of NSXMLDocument approprié) plutôt que d'assumer la responsabilité de l'analyse des fichiers XML. Si vous souhaitez le faire, lisez la suite. L'utilisation de différents noms d'éléments pour les éléments de sous-zone corrigera le problème de clobbing.

<book> 
    <title>Book 1</title> 
    <author> 
    <first> Jason </first> 
    <last> Alfonso. </last> 
    </author> 
    <summary> 
    <city> Milano </city> 
    <country> Italy </country> 
    </summary> 
</book> 

Cependant, il existe encore de meilleurs moyens.

De manière générale, pour analyser correctement un fichier XML, vous devez gérer une pile d'éléments en cours de traitement. Lorsque l'analyse d'un élément commence, vous créez un nouvel élément et l'ajoutez à la pile. Lorsque l'analyse d'un élément se termine, vous enlevez un élément de la pile et vous le donnez à l'élément situé en haut de la pile. Vous pouvez créer des éléments de différentes classes en fonction du nom de l'élément en utilisant une méthode usine (ci-dessous, -nodeWithTag:attributes:parser:) et un dictionnaire qui mappe les noms des éléments aux classes (ci-dessous, elementClasses).

/* category to return a default object (rather than nil) 
    when a key isn't present in a dictionary. 
*/ 
@interface NSDictionary (defaultObject) 
-(id)objectForKey:(NSString*)key default:(id)default; 
@end 

@implementation NSDictionary (defaultObject) 
-(id)objectForKey:(NSString*)key default:(id)default { 
    id object = [self objectForKey:key]; 
    if (nil == object) { 
     return default; 
    } 
    return object; 
} 
@end 

/* category to add aliases for stack operations 
    to NSMutableArray 
*/ 
@interface NSMutableArray (stack) 
-(void)push:(id)object; 
-(id)pop; 
-(id)top; 
@end 

@implementation NSMutableArray (stack) 
// could also use class_addMethod to alias push & top 
-(void)push:(id)object { 
    [self addObject:object]; 
} 
-(id)pop { 
    id last = [self lastObject]; 
    [self removeLastObject]; 
    return last; 
} 
-(id)top { 
    return [self lastObject]; 
} 
@end 


// the parser delegate. 
@interface ... <NSXMLParserDelegate> { 
    NSMutableArray activeElements; 
    id item; 
    ... 
@property (nonatomic,retain) item; 
@end 

@implementation ... 
@synthesize item; 

#pragma mark Class members 
// map element names to classes 
static NSDictionary *elementClasses; 

+(void)initialize { 
    nodeTypes=[[NSDictionary alloc] initWithObjectsAndKeys: 
     // Just an illustrative example of a custom class. 
     // You don't necessarily need a Book class. 
     [Book class],@"book", 
     nil]; 
} 

// if you have other init methods, make sure activeElements is created. 
-(id)init { 
    if ((self = [super init])) { 
     activeElements = [[NSMutableArray alloc] init]; 
     ... 
    } 
    return self; 
} 

-(void)parserDidStartDocument:(NSXMLParser *)parser { 
    // add sentinel element so stack isn't empty at start. 
    [activeElements push:[self nodeWithTag:@"root" attributes:nil parser:parser]]; 
} 

-(void)parserDidEndDocument:(NSXMLParser *)parser { 
    // The parser should ensure only case 1 is reachable, but still... 
    switch ([activeElements count]) { 
    case 0: 
     NSLog(@"Root element removed from stack early."); 
     break; 
    default: 
     NSLog(@"Extra elements in stack at parse end."); 
     [activeElements removeObjectsInRange:NSMakeRange(1, activeElements.count-1)]; 
     // FALLTHRU 
    case 1: 
     // top item should be the sentinel 
     self.item = [activeElements pop]; 
     if ([item.children count] == 1) { 
      // sentinel can safely be discarded if 
      self.item = [item.children objectAtIndex:0];  
     } 
     break; 
    } 
} 

#pragma mark Instance methods  
-(void) parser:(NSXMLParser *)parser 
didStartElement:(NSString *)elementName 
    namespaceURI:(NSString *)namespaceURI 
    qualifiedName:(NSString *)qName 
    attributes:(NSDictionary *)attributeDict 
{ 
    [activeElements push:[self nodeWithTag:elementName 
           attributes:attributeDict 
            parser:parser]]; 
} 

- (void)parser:(NSXMLParser *)parser 
didEndElement:(NSString *)elementName 
    namespaceURI:(NSString *)namespaceURI 
qualifiedName:(NSString *)qName 
{ 
    id element = [activeElements pop]; 
    if (element.attributes.count == 0 && element.children.count == 0) { 
     // simple leafs don't need to be Nodes. 
     [activeElements.top setValue:element.value forKey:elementName]; 
    } else { 
     [activeElements.top setValue:element forKey:elementName]; 
    } 
} 

-(void)parser:(NSXMLParser*)parser foundCharacters:(NSString*)string { 
    activeElements.top.value = string; 
} 

/* Factory method. Depending on elementName, create an 
    object of the appropriate type. 
*/ 
-(id)nodeWithTag:elementName attributes:attrs parser:(NSXMLParser*)parser { 
    id node =[[[elementClasses objectForKey:elementName 
            default:[NSMutableDictionary class]] 
         alloc] init]; 
    for (id key in attrs) { 
     @try { 
      [node setValue:[attrs objectForKey:key] forKey:key]; 
     } 
     @catch (NSException *exc) { 
      // TODO: warn user of invalid attribute(s) when parsing is finished 
    if ([exc name] == NSUndefinedKeyException) { 
       NSLog(@"%d,%d: Set attribute '%@' on a %@, but it doesn't have that property.", 
         [parser columnNumber], [parser lineNumber], 
         key, elementName); 
    } else { 
       NSLog(@"%d,%d: Caught %@ when setting %@ on a %@.", 
         [parser columnNumber], [parser lineNumber], 
         [exc name], key, elementName); 
      } 
     } 
    } 
    return [node autorelease]; 
} 

-(void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError { 
    NSLog(@"parse error: %@", parseError); 
    [self abort]; 
} 
-(void)parser:(NSXMLParser *)parser validationErrorOccurred:(NSError *)validError { 
    NSLog(@"validation error: %@", validError); 
    [self abort]; 
} 

-(void)abort { 
    [activeElements removeAllObjects]; 
} 

Un autre bug

Jetez un oeil près:

} else if ([elementName isEqualToString:@"subfield"]) { 
    if ([[attributeDict valueForKey:@"id"] isEqualToString:@"a"]) { 
     self.authorName1 = [[NSMutableString alloc] init]; 
    } 
} else if ([elementName isEqualToString:@"subfield"]) { 
    if ([[attributeDict valueForKey:@"id"] isEqualToString:@"b"]) { 
        self.authorName2 = [[NSMutableString alloc] init]; 
    } 
} 

Si le premier test réussit, vous ne serez jamais atteindre la seconde. Le correctif trivial ici est de combiner les blocs, bien que cela aura encore la question clobber:

} else if ([elementName isEqualToString:@"subfield"]) { 
    if ([[attributeDict valueForKey:@"id"] isEqualToString:@"a"]) { 
     self.authorName1 = [[NSMutableString alloc] init]; 
    } else if ([[attributeDict valueForKey:@"id"] isEqualToString:@"b"]) { 
     self.authorName2 = [[NSMutableString alloc] init]; 
    } 
} 
2

Télécharger: https://github.com/Insert-Witty-Name/XML-to-NSDictionary

Ensuite, vous faites simplement:

NSDictionary *dic = [XMLReader dictionaryForPath:filepath error:nil]; 

Le résultat est un NSDictionary * dic avec les dictionnaires, les tableaux et les chaînes à l'intérieur, en fonction de l'XML:

{ 
    book =  { 
     author =   { 
      first = Jason; 
      last = "Alfonso."; 
     }; 
     summary =   { 
      city = Milano; 
      country = Italy; 
     }; 
     title = "Book 1"; 
    }; 
}