2013-05-09 114 views
2

是否有可能在NSXMLparser中提供路徑表達式?我有一個XML文件,它具有幾個相同的名稱標籤,但它們位於不同的元素中。有什麼辦法可以區分它們嗎?這裏是XML:使用NSXMLparser,你如何解析具有相同名稱標籤但在不同元素中的xml文件?

<Schools> 
     <School> 
      <ID>335823</ID> 
      <Name>Fairfax High School</Name> 
      <Student> 
       <ID>4195653</ID> 
       <Name>Will Turner</Name> 
      </Student> 
      <Student> 
       <ID>4195654</ID> 
       <Name>Bruce Paltrow</Name> 
      </Student> 
      <Student> 
       <ID>4195655</ID> 
       <Name>Santosh Gowswami</Name> 
      </Student> 
     </School> 
    <Schools> 
+0

您的XML在最後的結束標記上也缺少'/'。 – Rob 2013-05-09 15:42:15

回答

3

我會創建單獨的SchoolStudent對象。您的解析器將具有currentSchoolcurrentStudent的屬性。每當你的解析器打<Student>標籤,撥打

self.currentStudent = [[MyStudentObject alloc] init]; 

每當你的解析器打</Student>標籤,撥打

self.currentStudent = nil; 

然後,當你打<name>標籤,你可以檢查看看,如果你有一個currentStudent。如果你這樣做,那麼這個名字就是那個學生的名字。如果沒有現在的學生,那麼這個名字就是學校的名字。

if (self.currentStudent) 
{ 
    self.currentStudent.name = /*string between <name> tags*/ 
} 
else 
{ 
    self.currentSchool.name = /*string between <name> tags*/ 
} 

對不起我的代碼片段是如此短暫,我沒有太多的時間,現在鍵入此。如果需要更多細節,我可以稍後添加更多代碼。


UPDATE

對我來說,進入更詳細的最快方法是隻是爲了顯示我正在尋找的代碼,並把一切都解釋成代碼中的註釋。如果對此有任何疑問,或者需要進一步解釋,請告訴我要詳細說明的內容,我會盡我所能。

StudentXML.h

#import <Foundation/Foundation.h> 

@interface StudentXML : NSObject 

@property (nonatomic, strong) NSString *ID;  // MAKE SURE THIS EXACTLY MATCHES THE ELEMENT IN THE XML!! 
@property (nonatomic, strong) NSString *Name; // MAKE SURE THIS EXACTLY MATCHES THE ELEMENT IN THE XML!! 

@end 

StudentXML.m

#import "StudentXML.h" 

@implementation StudentXML 

@end 

SchoolXML.h

#import <Foundation/Foundation.h> 
#import "StudentXML.h" 

@interface SchoolXML : NSObject 

@property (nonatomic, strong) NSString *ID;  // MAKE SURE THIS EXACTLY MATCHES THE ELEMENT IN THE XML!! 
@property (nonatomic, strong) NSString *Name; // MAKE SURE THIS EXACTLY MATCHES THE ELEMENT IN THE XML!! 
@property (nonatomic, strong) NSMutableArray *studentsArray; // Array of StudentXML objects 

@end 

SchoolXML.m

#import "SchoolXML.h" 

@implementation SchoolXML 

// Need to overwrite init method so that array is created when new SchoolXML object is created 
- (SchoolXML *) init; 
{ 
    if (self = [super init]) 
    { 
     self.studentsArray = [[NSMutableArray alloc] init]; 

     return self; 
    } 
    else 
    { 
     NSLog(@"Error - SchoolXML object could not be initialized in init on SchoolXML.m"); 
     return nil; 
    } 
} 

@end 

SchoolsParser.h

#import <Foundation/Foundation.h> 
#import "SchoolXML.h" 
#import "StudentXML.h" 

@interface SchoolsParser : NSObject 
{ 
    NSMutableString *currentElementValue; // Will hold the string between tags until we decide where to put it 
} 

@property (nonatomic, strong) SchoolXML *currentSchool;  // Will hold the school that is in the process of being filled 
@property (nonatomic, strong) StudentXML *currentStudent; // Will hold the student that is in the process of being filled 
@property (nonatomic, strong) NSMutableArray *allSchools; // This is the final list of all the data in the XML file 

- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict; 
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string; 
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName; 

@end 

SchoolsParser。米

#import "SchoolsParser.h" 

@implementation SchoolsParser 

- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict 
// This method will be hit each time the parser sees an opening tag 
// elementName is the string between the <> (example "School") 
{ 
    if ([elementName isEqualToString:@"School"]) 
    { 
     self.currentSchool = [[SchoolXML alloc] init]; 
    } 
    else if ([elementName isEqualToString:@"Student"]) 
    { 
     self.currentStudent = [[StudentXML alloc] init]; 
    } 
} 

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string 
// This method will be hit each time the parser sees a string between tags 
// string is the value between the open and close tag (example "Fairfax High School") 
// We take string and hold onto it until we can decide where it should be put 
{ 
    currentElementValue = [[NSMutableString alloc] initWithString:string]; 
} 

- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName 
// This method will be hit each time the parser sees an closing tag 
// elementName is the string between the </> (example "School") 
// This is the method where we decide where we want to put the currentElementValue string 
{ 
    if ([elementName isEqualToString:@"Student"]) 
    { 
     // Put the current student into the studentsArray of the currentSchool 
     [self.currentSchool.studentsArray addObject:self.currentStudent]; 

     // We've finished building this student and have put it into the school we wanted, so we clear out currentStudent so we can reuse it next time 
     self.currentStudent = nil; 
    } 
    else if ([elementName isEqualToString:@"School"]) 
    { 
     // Put the current school into the allSchoolsArray to send back to our view controller 
     [self.allSchools addObject:self.currentSchool]; 

     // We've finished building this school and have put it into the return array, so we clear out currentSchool so we can reuse it next time 
     self.currentSchool = nil; 
    } 
    else if ([elementName isEqualToString:@"Schools"]) 
    { 
     // We reached the end of the XML document 
     return; 
    } 
    else 
    // This is either a Name or an ID, so we want to put it into the correct currentSomething we are building 
    { 
     if (self.currentStudent) 
     // There is a currentStudent, so the Name or ID we found is that of a student 
     { 
      // Since the properties of our currentStudent object exactly match the elementNames in our XML, the parser can automatically fills values in where they need to be without us doing any more 
      // For example, it will take "Will Turner" in the <Name> tags in the XML and put it into the .Name property of our student 
      [self.currentStudent setValue:currentElementValue forKey:elementName]; 
     } 
     else 
     // There was no student, so the Name or ID we found is that of a school 
     { 
      // Since the properties of our currentStudent object exactly match the elementNames in our XML, the parser can automatically fills values in where they need to be without us doing any more 
      // For example, it will take "Fairfax High School" in the <Name> tags in the XML and put it into the .Name property of our school 
      [self.currentSchool setValue:currentElementValue forKey:elementName]; 
     } 
    } 

    // We've now put the string in currentElementValue where we wanted it, so we clear out currentElementValue so we can reuse it next time 
    currentElementValue = nil; 
} 

-(void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError 
{ 
    NSLog(@"Error in SchoolsParser.m"); 
    NSLog(@"%@",parseError.description); 
} 

@end 

UIViewController.m要開始解析(請確保您#include SchoolXML,StudentXML和SchoolsParser):

- (void) startSchoolParser 
{ 
    NSXMLParser *nsXmlParser = [[NSXMLParser alloc] initWithData:responseData]; // where "responseData" is the NSData object that holds your XML 
    SchoolsParser *parser = [[SchoolsParser alloc] init]; 
    [nsXmlParser setDelegate:parser]; 
    if ([nsXmlParser parse]) 
    { 
     // Parsing was successful 
     NSArray *allSchools = parser.allSchools; 
     // You can now loop through allSchools and use the data how ever you want 

     // For example, this code just NSLog's all the data 
     for (SchoolXML *school in allSchools) 
     { 
      NSLog(@"School Name = %@",school.Name); 
      NSLog(@"School ID = %@",school.ID); 
      for (StudentXML *student in school.studentsArray) 
      { 
       NSLog(@"Student Name = %@",student.Name); 
       NSLog(@"Student ID = %@",student.ID); 
      } 
     } 
    } 
    else 
    { 
     NSLog(@"Parsing Failed"); 
    } 
} 
+0

請詳細說明,因爲我不明白我如何在單個NSDictionnary中添加所有元素。 – 2013-05-09 13:59:52

+0

@ user2299789:您可以將NSDictionaries嵌套到另一箇中,因此如果這是您需要的,您總是可以最終生成一個包含所有內容的最終NSDictionary。你可以使用解析器的.m文件中的代碼更新你的問題嗎?我想看看你是如何使用解析器委託方法。 – GeneralMike 2013-05-09 14:07:07

+0

是的,這是我的問題,但直到現在我沒有寫解析器。文件,因爲我沒有找到解決方案。 – 2013-05-09 15:44:04

1

1)當解析器遇到School標籤時初始化一個數組,這個數組將保存你要創建的所有Student對象。

2)每次看到學生開始標籤時創建學生對象。

3)然後分析器遇到ID標籤解析成[studentObj SETID:parsedContent]

4)然後分析器遇到名稱標記[studentObj的setName:parsedContent]

5)現在分析器遇到學生標籤的端,現在將此studentObj添加到您在步驟1中初始化的數組中

1

一種方法是隻具有兩個BOOL類屬性/高德,一個針對學校,另一個針對學生。然後在didStartElement中,如果elementName@"School"@"Student",那麼設置合適的布爾屬性/ ivar。同樣,在didEndElement中,如果elementName@"School"@"Student",則清除相應的布爾屬性/ ivar。然後,在解析@"Name"時,可以檢查這兩個布爾屬性/ ivars以查看您正在解析哪一個屬性並採取適當的步驟。 (例如,如果學生的布爾值爲true,那麼顯然這個名字是學生,如果學生布爾值爲假,但學校布爾值爲true,那麼名稱就是學校的名稱。)

還有更多解決問題的優雅方法(例如XML Node Parser),但這可能是最簡單的。


順便說一句,我不知道,如果您有任何發言權在XML的結構,但我認爲這是最好的,如果所有的數組都被包裹自己的元素中。因此而不是:

<Schools> 
    <School> 
     <ID>335823</ID> 
     <Name>Fairfax High School</Name> 
     <Student> 
      <ID>4195653</ID> 
      <Name>Will Turner</Name> 
     </Student> 
     <Student> 
      <ID>4195654</ID> 
      <Name>Bruce Paltrow</Name> 
     </Student> 
     <Student> 
      <ID>4195655</ID> 
      <Name>Santosh Gowswami</Name> 
     </Student> 
    </School> 
<Schools> 

我寧願看到包含學生列表的「學生」元素名稱。您最後關閉的Schools標籤也缺少/

<Schools> 
    <School> 
     <ID>335823</ID> 
     <Name>Fairfax High School</Name> 
     <Students> 
      <Student> 
       <ID>4195653</ID> 
       <Name>Will Turner</Name> 
      </Student> 
      <Student> 
       <ID>4195654</ID> 
       <Name>Bruce Paltrow</Name> 
      </Student> 
      <Student> 
       <ID>4195655</ID> 
       <Name>Santosh Gowswami</Name> 
      </Student> 
     </Students> 
    </School> 
</Schools> 

您可以編寫處理前的分析器,但是當你進入更動態生成的結果的世界裏,有XML更準確地反映底層數據的結構是非常有用的。我知道當你爲這個XML feed編寫一個非常具體的解析器時,它可能看起來並不相關,但它確實是一個更合理的結構。

+0

是的,用這種方法,我可以添加所有elemebt在一個NSDictionnary? – 2013-05-09 14:03:50

+0

有了這個設置,你真的只需要1個'BOOL',對於學生來說,對吧?假設名稱不是學生的名字,它必須是學校的名字。 – GeneralMike 2013-05-09 14:10:04

+0

@GeneralMike - 顯然,是的,你只需要這個XML的一個布爾值。 (事實上​​,當你有你的結構來解析和填充你的樹的節點時,你不需要任何)。我只是想讓它變得非常明顯而且非常簡單。並且,通過使用兩個布爾值,您可以更強大一些,能夠檢測格式不正確的XML,並且能夠處理如果'Schools'標籤後來嵌入到具有自己名稱的「區域」結構中(例如,人們經常給我們簡化格式的XML)等。 – Rob 2013-05-09 14:12:27

相關問題