2011-01-02 13 views
1

Ich habe eine XML-Datei, siehe unten. Ich verwende NSXMLParser, aber ich kann meinen Autor und meine Zusammenfassung nicht analysieren. Aufgrund von Zugriffsrechten kann ich die XML-Datei nicht bearbeiten.XML-Parser (Objective C)

Irgendeine Lösung?

XML-Datei:

<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> 

Mein 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]; 
     } 
    } 
} 

ich bin der Lage, Unterfeld zu greifen. aber auch das Unterfeld von summary wird während des Autors benutzt, was ich nicht mache. Ich brauche beide

+2

Zeigen Sie den Code, den Sie ausprobiert haben. Weitere Informationen finden Sie unter ["Umgang mit XML-Elementen und -Attributen"] (http://developer.apple.com/library/ios/ipad/#documentation/cocoa/Conceptual/XMLParsing/Articles/HandlingElements.html) in den Apple-Dokumenten und Beispielcode. – Anna

+1

zumindest Code anzeigen. Apple hat mehrere Beispiele für den Umgang mit XML, die Sie überprüft haben? –

+1

Für weitere Fragen zu SO, lesen Sie ["Die perfekte Frage schreiben"] (http://tinyurl.com/so-hints) und ["Die kurze, unabhängige, korrekte (kompilierbar), Beispiel"] (http : // sscce.org /). – outis

Antwort

4

Hinweis getrennt werden, dass es einfacher sein könnte, um Ihre Anwendung auf Core Data oder NSXML (unter Verwendung eines geeigneten init method of NSXMLDocument) zu stützen, anstatt Verantwortung zu übernehmen, die XML-Dateien für die Analyse. Wenn Sie dies wünschen, lesen Sie weiter.

Durch die Verwendung verschiedener Elementnamen für die Unterfeldelemente wird das Problem der Clobberbildung behoben.

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

Allerdings gibt es noch bessere Möglichkeiten.

Um eine XML-Datei korrekt analysieren zu können, müssen Sie im Allgemeinen einen Stapel von Elementen in Bearbeitung halten. Wenn das Parsen eines Elements beginnt, erstellen Sie ein neues Element und fügen es dem Stapel hinzu. Wenn das Parsen eines Elements abgeschlossen ist, blenden Sie ein Element aus dem Stapel und geben es dem Element jetzt oben auf dem Stapel. Sie können Elemente verschiedener Klassen basierend auf dem Elementnamen erstellen, indem Sie eine Factory-Methode (unter -nodeWithTag:attributes:parser:) und ein Wörterbuch verwenden, das Elementnamen Klassen zuordnet (unten, 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]; 
} 

Ein weiterer Fehler

Werfen Sie einen Blick auf:

} 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]; 
    } 
} 

Wenn der erste Test erfolgreich ist, werden Sie nie die zweite erreichen. Die triviale fix hier ist es, die Blöcke zu kombinieren, obwohl dies immer noch die clobber Problem haben:

} 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]; 
    } 
}