Parse XML ด้วย NSXMLParser

มี library หลากหลายตัวที่ช่วยให้เราสามารถ parse XML ด้วย Objective-C ได้ ซึ่งคลาสในตระกูล NS* ก็เตรียมNSXMLParser ให้เราได้ใช้อยู่แล้ว

หลักการคร่าวๆของ NSXMLParser จะไม่ใช่การเขียนโปรแกรมวนรอบให้วิ่งไปตาม element ต่างๆที่เจอ แต่จะเป็นการเขียน code เข้าไปแทรกที่จังหวะต่างๆใน delegate method ที่มีให้ ในมุมมองที่ซึ่งตัว parser จะวิ่งไปตาม element ต่างๆให้เราเอง ส่วนเราก็มีหน้าที่วาง code แทรกลงไป ว่าหาก parser วิ่งมาถึงจุดนี้ จะให้มันทำอะไรบ้าง นั่นเอง

สำหรับบันทึกนี้จะเอา code เข้าไปเขียนไว้ในเหตุการณ์ต่างๆดังนี้
เมื่อ parser วิ่งไปเจอ element (เจอ tag เปิด)
เมื่อ parser วิ่งไปเจอ character ระหว่าง element
เมื่อ parser วิ่งออกจาก element (เจอ tag ปิด)
เมื่อ parser วิ่งไปจนจบ document

ทั้ง 4 เหตุการณ์ดังกล่าวจะถูกแทนด้วย delegate method ต่อไปนี้ตามลำดับ

parser:didStartElement:namespaceURI:qualifiedName:attributes:
parser:foundCharacters:
parser:didEndElement:namespaceURI:qualifiedName:
parserDidEndDocument:

ชื่อ method ยาวหน่อย 😉

ครานี้ก็มาถึง code ตัวอย่างการใช้งาน โดยจะใช้ XML ตัวอย่างจาก W3C ไฟล์นี้ โดยเริ่มจากการประกาศตัวแปรต่างเตรียมใช้งานก่อน ซึ่งจากลักษณะของ xml ตัวอย่าง จะได้ตัวแปรต่างๆดังนี้

    /* สำหรับเก็บรายชื่อของ food */
    NSMutableArray *breakfast_menu;
    /* เก็บชื่อของ element ที่ตัว parser วิ่งไปถึง */
    NSString *currentElement
    /* อาหารแต่ละชนิดจะมี property ของตัวเอง เราจึงเลือกใช้ dictionary */
    NSMutableDictionary *currentFood;
    /* เอาไว้เก็บค่าชั่วคราว */
    NSMutableString *currentName, *currentPrice, *currentDescription, *currentCalories;

จากนั้นเริ่มลงมือ โดยการสร้าง instance ของ NSXMLParser ก่อนด้วย xml ตัวอย่างดังกล่าว พร้อมทั้งกำหนด delegate ให้เรียบร้อย ดังนี้

    NSString *path = @”http://www.w3schools.com/XML/simple.xml”;
    breakfast_menu = [[NSMutableArray alloc] init];
    NSURL *xmlURL = [NSURL URLWithString: path];
    parser = [[NSXMLParser alloc] initWithContentsOfURL: xmlURL];
    [parser setDelegate: self];
    NSLog(@”start parsing”);
    [parser parse];

แทรก code เข้าไป เมื่อ parser วิ่งไปเจอ element ใดๆ ในที่นี้จะกำหนดว่าหาก parser วิ่งไปเจอ element ชื่อ food ให้สร้าง object ต่างๆไว้รอเก็บ property ต่างๆของ food ไว้เลย

 – (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{
    if([elementName isEqualToString: @”food”]){
        currentFood = [[NSMutableDictionary alloc] init];
        currentName = [[NSMutableString alloc] init];
        currentPrice = [[NSMutableString alloc] init];
        currentDescription = [[NSMutableString alloc] init];
        currentCalories = [[NSMutableString alloc] init];
    }
}

และเมื่อ parser วิ่งไปเจอ character ที่อยู่ระหว่าง tag ก็ให้ตรวจสอบดูว่าตอนนี้กำลังอ่านอยู่ใน element ชื่อว่าอะไร จากนั้นให้เอาค่าที่ได้เก็บไว้ในตัวแปรให้ถูกต้อง

– (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
    if(![[string stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]] isEqualToString: @””]){
        if([currentElement isEqualToString: @”name”]){
            [currentName appendString: string];
        }else if([currentElement isEqualToString: @”price”]){
            [currentPrice appendString: string];
        }else if([currentElement isEqualToString: @”description”]){
            [currentDescription appendString: string];
        }else if([currentElement isEqualToString: @”calories”]){
            [currentCalories appendString: string];
        }
    }
}

และเมื่อ parser วิ่งไปจนจบ element นั้นๆแล้ว ให้นำค่าที่อ่านได้นำไปเก็บไว้ใน food ที่เป็น dictionary object แต่หากพบว่า parser วิ่งออกจาก element ชื่อ food ให้นำตัวแปร currentFood ไปเก็บไว้ใน list ที่เตรียมไว้ตอนแรก

– (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{
    if([elementName isEqualToString: @”name”]){
        [currentFood setObject: currentName forKey: @”name”];
    }else if([elementName isEqualToString: @”price”]){
        [currentFood setObject: currentPrice forKey: @”price”];
    }else if([elementName isEqualToString: @”description”]){
        [currentFood setObject: currentDescription forKey: @”description”];
    }else if([elementName isEqualToString: @”calories”]){
        [currentFood setObject: currentCalories forKey: @”calories”];
    }else if([elementName isEqualToString: @”food”]){
        [breakfast_menu addObject: currentFood];
    }
}

เมื่อ parser วิ่งไปจนจบ document แล้วให้ลอง print ออกมาดู ดังนี้

– (void)parserDidEndDocument:(NSXMLParser *)parser {
    NSLog(@”all done!”);
    NSLog(@”food list array has %d items”, [breakfast_menu count]);
    NSMutableDictionary *food;
    for(int i = 0; i < [breakfast_menu count]; i ++){
        food = [breakfast_menu objectAtIndex: i];
        NSLog(@”\nname: %@\nprice: %@\ndescription: %@\ncalories: %@\n\n”,
              [food objectForKey: @”name”],
              [food objectForKey: @”price”],
              [food objectForKey: @”description”],
              [food objectForKey: @”calories”]);
    }
}
credit : http://khomkrit.blogspot.com
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s