มี 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 ตัวอย่าง จะได้ตัวแปรต่างๆดังนี้
จากนั้นเริ่มลงมือ โดยการสร้าง instance ของ NSXMLParser ก่อนด้วย xml ตัวอย่างดังกล่าว พร้อมทั้งกำหนด delegate ให้เรียบร้อย ดังนี้
แทรก code เข้าไป เมื่อ parser วิ่งไปเจอ element ใดๆ ในที่นี้จะกำหนดว่าหาก parser วิ่งไปเจอ element ชื่อ food ให้สร้าง object ต่างๆไว้รอเก็บ property ต่างๆของ food ไว้เลย
และเมื่อ parser วิ่งไปเจอ character ที่อยู่ระหว่าง tag ก็ให้ตรวจสอบดูว่าตอนนี้กำลังอ่านอยู่ใน element ชื่อว่าอะไร จากนั้นให้เอาค่าที่ได้เก็บไว้ในตัวแปรให้ถูกต้อง
และเมื่อ parser วิ่งไปจนจบ element นั้นๆแล้ว ให้นำค่าที่อ่านได้นำไปเก็บไว้ใน food ที่เป็น dictionary object แต่หากพบว่า parser วิ่งออกจาก element ชื่อ food ให้นำตัวแปร currentFood ไปเก็บไว้ใน list ที่เตรียมไว้ตอนแรก
เมื่อ parser วิ่งไปจนจบ document แล้วให้ลอง print ออกมาดู ดังนี้
code ตัวอย่างดาวน์โหลดได้จากที่นี่
Related Link from Roti
หลักการคร่าวๆของ 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"]);
}
}
code ตัวอย่างดาวน์โหลดได้จากที่นี่
Related Link from Roti
ขอบคุณมากๆ ครับ ^^'
ReplyDeleteเป็นประโยชน์มากครับ
ReplyDelete