1. Trang chủ
  2. » Công Nghệ Thông Tin

Phát triển ứng dụng cho iPhone và iPad - part 33 pps

10 268 0
Tài liệu đã được kiểm tra trùng lặp

Đang tải... (xem toàn văn)

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 10
Dung lượng 2,9 MB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

Here is the parser:didStartElement: namespaceURI:qualifiedName:attributes: method implementation: // Called when the parser encounters a start element - void parser:NSXMLParser *parser d

Trang 1

// Called when the parser finishes parsing the document

- (void)parserDidEndDocument:(NSXMLParser *)parser { NSLog (@”parserDidEndDocument”);

}

RSSSampleViewController.m

The code is relatively straightforward First, for illustrative purposes, you log the name of the method that you are executing This will prove instructive when you look at the console log

Examining the order in which the parser calls the delegate methods will help you to better understand how the parser works Additionally, it is useful for debugging purposes should you encounter an error in your parsing logic

In the parserDidStartDocument : method, you also initialize the inItemElement fl ag to NO because you are not currently in an item element In the parserDidEndDocument method, you log that you have reached the end of the document

Next, you implement the start and end element functions Here is the parser:didStartElement:

namespaceURI:qualifiedName:attributes: method implementation:

// Called when the parser encounters a start element

- (void) parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict { NSLog (@”didStartElement”);

// Check to see which element we have found

if ([elementName isEqualToString:@”item”]) { // We are in an item element

inItemElement = YES;

} // If we are in an item and found a title

if (inItemElement & & [elementName isEqualToString:@”title”]) { // Initialize the capturedCharacters instance variable capturedCharacters = [[NSMutableString alloc] initWithCapacity:100];

} }

RSSSampleViewController.m

This method receives the name of the element that has started as a parameter You check the name of the element and set the inItemElement fl ag if you have started an item element Next, check to see if you have started a title element You may be wondering why you are checking

to see if you have started a title element when you already know that you have started an item

Trang 2

element Remember that the parser calls this method each time an element begins Because XML is

typically organized as a tree data structure, you will almost certainly have elements nested within

other elements Therefore, each time that the parser calls this method, you could be starting a new

element without having ended another element

Notice that when you check for starting a title element, you also verify that you are in an item

element If you encounter a title element and you are not in an item , you don ’ t need to do

anything If, however, you hit a title element and you are already in an item element, you initialize

the capturedCharacters instance variable You will use this NSMutableString to aggregate the

characters from the title element in the call to parser:foundCharacters:

You will do some similar processing in the didEndElement method:

// Called when the parser encounters an end element

- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName

namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {

NSLog (@”didEndElement”);

// Check to see which element we have ended

// If we are in an item and ended a title

if (inItemElement & & [elementName isEqualToString:@”title”]) {

NSLog (@”capturedCharacters: %@” , capturedCharacters);

self.textView.text = [self.textView.text

stringByAppendingFormat:@”%@\n\n”,capturedCharacters];

// Release the capturedCharacters instance variable

[capturedCharacters release];

capturedCharacters = nil;

}

if ([elementName isEqualToString:@”item”]) {

// We are no longer in an item element

inItemElement = NO;

}

}

RSSSampleViewController.m

First, check to see if you are in an item element and are ending a title element If so, log the

characters that you captured, append them to the textView , and release the capturedCharacters

instance variable because you are fi nished with it for now Next, check to see if the parser called

this element because you are ending an item element If that is the case, you set the inItemElement

fl ag to NO Remember that the parser will call this method each time an element ends It is up to the

developer to determine what element has ended and to act accordingly

Trang 3

The last delegate method that you will implement is parser : foundCharacters : In this method, you simply check to make sure that the capturedCharacters variable is not nil , and then append the characters passed into the method to the capturedCharacters mutable string Keep in mind that the parser may call this method multiple times for the text inside of a single element That is why you are using a mutable string and appending the characters to it each time that the parser calls this method Here is the implementation:

// Called when the parser finds characters contained within an element

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {

if (capturedCharacters != nil) { [capturedCharacters appendString:string];

} }

RSSSampleViewController.m

You are now ready to build and run the application As long as you have an active Internet connection, the application should download and parse the RSS feed and display the top headlines from CNN.com in the text view

Generating XML with libxml

Sometimes when you are building a connected application, you will need to generate XML from scratch Thus far, you have only explored consuming XML In this section, you learn how to build XML dynamically at runtime

There are a several ways to build XML in your application You should choose an appropriate method based on the needs of your application

If you need to send a specifi c XML message with only limited dynamic data, your best bet is to build a format string using the XML and placeholders for your variables For instance, if you were implementing an online address book, a message that you send to the server to add an entry may look something like this:

< address >

< name > John Smith < /name >

< street > 1 Ocean Blvd < /street >

< city > Isle Of Palms < /city >

< state > SC < /state >

< zip > 29451 < /zip >

< /address >

In this case, there is no need to build the XML document structure at runtime because you already know the structure of the message at compile time Simply use a format string like this:

NSString *s = [[NSString alloc] initWithFormat:

“ < address > ” “ < name > %@ < /name > ”

Trang 4

“ < city > %@ < /city > ”

“ < state > %@ < /state > ”

“ < zip > %@ < /zip > ”

“ < /address > ”,name,street,city,state,zip];

On the other hand, if you are building an order entry system where the number of items in an order

could change based on conditions in the application at runtime, you probably will need to generate

the entire XML tree dynamically While generating XML with the NSXMLParser is possible as

outlined in Apple ’ s “ Event - Driven XML Programming Guide for Cocoa, ” it is diffi cult and not

intuitive

It is far easier to build an XML document using the DOM model for XML parsing Apple does not

provide a DOM parser on the iPhone, but there is a C XML library called libxml installed on the

iPhone that you can use for DOM processing It is free and available under the MIT license, which

you can fi nd at http://w w w.opensource.org/licenses/mit-license.html

While generating XML dynamically at runtime, using libxml2 is far easier than using NSXMLParser

Unfortunately, the library is in C, so it is a little more diffi cult to work with than an Objective - C

library If you think that you will be building dynamic XML often, you may want to consider

wrapping libxml in your own Objective - C wrappers Additionally, there are several open source

wrappers for libxml; however, you will not be looking at them You will better understand how to

use libxml by using the library directly in your code

Using libxml in your project is as simple as adding the libxml2.dylib to your project and editing

your header search paths so that the compiler can fi nd the headers Then you only need to include

the headers that you plan to use in your source code and you are ready to go

You may also want to use libxml if your application needs the capabilities of a DOM parser as

opposed to the Apple provided SAX parser The libxml library also supports XPath XPath is a

query language that you can use to search your XML Additionally, libxml also supports validating

XML fi les against a DTD (Document Type Defi nition) You can think of a DTD as the specifi cation

for the XML used in a document

XML Generation Sample

In this section, you will build a very simple application that generates a simple XML document tree,

as shown in Figure 10 - 7 The XML that you generate will look like this:

< ?xml version=”1.0”? >

< rootNode >

< ChildNode1 > Child Node Content < /ChildNode1 >

< AttributedNode attribute1=”First” attribute2=”Second” >

< AttributedChild > Attributed Node Child Node < /AttributedChild >

< /AttributedNode >

< AttachedNode > Attached Node Text < /AttachedNode >

< ! This is an XML Comment >

< /rootNode >

Trang 5

Granted, this is not the most complex XML document, but the code will demonstrate everything that you will need to generate XML documents of limitless complexity

Create a new View - based Application called MakeXML The fi rst thing that you have to do is tell Xcode that you will be using the libxml library Right - click on the project in the left pane of Xcode and select

Add New Group Call the new group C Libs Next, add a reference to the

libxml2.dylib dynamic library Right - click on the new group and select Add ➪ Existing Frameworks In the dialog, select Dylibs from the drop down and select libxml2.dylib You should see libxml2.dylib appear

in the C Libs group in Xcode

You now have to alter the search path in Xcode so that Xcode can fi nd the headers for libxml2 when you are ready to compile Open the project build settings by selecting Project ➪ Edit Project Settings from the menu bar In the Search Paths area, add the path /Developer/Platforms/

iPhoneOS.platform/Developer/SDKs/iPhoneOS3.1.3.sdk/usr/

include/libxml2 to the Header Search Paths item Enable Xcode to search this directory and below by checking the recursive box Note that you will have to change the SDK in the path based on which SDK you are compiling with This example assumes that you are using the iPhone SDK version 3.1.3 and that you have installed the SDK in the default location

Now you will build the UI for the application, as shown in Figure 10 - 8

In the MakeXMLViewController.h header, add a UITextView instance variable and outlet Add a method generateXML that you will call to generate the XML Here is the complete MakeXMLViewController.h header:

#import < UIKit/UIKit.h >

rootNode

attribute1

attribute2

AttributedChild

FIGURE 10 - 7: Tree representation of generated XML

FIGURE 10 - 8: MakeXML sample UI

Trang 6

UITextView *textView;

}

@property (nonatomic, retain) IBOutlet UITextView *textView;

-(void) generateXML;

@end

MakeXMLViewController.h

In the MakeXMLViewController.m implementation fi le, synthesize the textView property:

@synthesize textView;

Next, implement viewDidUnload and dealloc to clean up the textView property and instance

variable:

- (void)viewDidUnload {

// Release any retained subviews of the main view.

// e.g self.myOutlet = nil;

self.textView=nil;

}

- (void)dealloc {

[textView dealloc];

[super dealloc];

}

MakeXMLViewController.m

Open the MakeXMLViewController.xib fi le in Interface Builder and add a UITextView control

Resize the control to take up the whole screen Connect the UITextView in Interface Builder to the

textView property in File ’ s Owner

In the MakeXMLViewController.m implementation fi le, add the import statements for the libxml

parser and libxml tree headers:

#import < libxml/parser.h >

#import < libxml/tree.h >

You will construct the XML in the generateXML method I want to demonstrate a few different

things in this method, so it ’ s long Let ’ s walk through the method section by section, and then I will

show the whole method in its entirety

You fi rst declare an xmlDocPtr object xmlDocPtr is a typedef for a pointer to the libxml xmlDoc

structure This structure represents the entire XML document that you will be creating

- (void) generateXML {

xmlDocPtr doc;

Trang 7

Every XML document consists of a set of nodes At the top of the tree of nodes is a special node

called the root node You need to declare the root node as an xmlNodePtr object xmlNodePtr is

a typedef to a pointer to an xmlNode struct The library uses the xmlNode struct to represent all XML nodes Here is the declaration of the root node:

xmlNodePtr rootNode;

Next, you call the xmlNewDoc library function to create a new document model You assign the return to the doc object:

// Create a new xml document doc = xmlNewDoc(BAD_CAST “1.0”);

You may have noticed the frightening looking BAD_CAST parameter in the function call BAD_CAST

is simply a macro defi ned in the xmlstring.h header that you can use to cast a string to the

xmlChar* type You will likely use BAD_CAST anywhere that you use a string object with the library Remember that libxml is a C library so you should omit the @ symbol that you use when defi ning

NSString objects in Objective - C In this case, you are using a C - style char* string

The next step is to create the root node You call the xmlNewNode function to create a new node The method takes an xmlNsPtr and an xmlChar* as its parameters You can use the xmlNsPtr parameter

to specify a namespace for your node instance You use namespaces to ensure that the XML elements defi ned in your document do not confl ict with elements with the same name in other XML documents You will not be using namespaces in this sample The xmlChar* is the name

of your new node In this case, let ’ s call the node rootNode :

// Create the root node rootNode = xmlNewNode(NULL, BAD_CAST “rootNode”);

Because the root element is the starting point for the document, you must explicitly set the root element Call the xmlDocSetRootElement function and pass in a pointer to the document and the root node:

xmlDocSetRootElement(doc, rootNode);

Now you are ready to start adding additional nodes to the tree The easiest way to add new nodes

to your XML document tree is to use the xmlNewChild function This function accepts a pointer to the parent node for the new node, a namespace pointer if you are using namespaces, the name of the new node, and the textual content of the node You create ChildNode1 as follows:

// Create a new child off the root xmlNewChild(rootNode, NULL, BAD_CAST “ChildNode1”, BAD_CAST “Child Node Content”);

This statement creates a node called ChildNode1 that is a child of the rootNode and contains the string Child Node Content Again, you can see that you use the BAD_CAST macro to pass string data into the library

Trang 8

Next, you create a node that contains XML attributes Attributes are another way to add data to

an element node Because you will append a child to this node, you declare an xmlNodePtr so that

you can hold a reference to the new node Then, you use the xmlNewChild function that you have

already seen to create a new node You will call the xmlNewProp function to add two attributes to

the attributedNode , as follows:

// Add a node with attributes

xmlNodePtr attributedNode;

attributedNode = xmlNewChild (rootNode, NULL,

BAD_CAST “AttributedNode”, NULL);

xmlNewProp(attributedNode, BAD_CAST “attribute1”, BAD_CAST “First”);

xmlNewProp(attributedNode, BAD_CAST “attribute2”, BAD_CAST “Second”);

MakeXMLViewController.m

So far, you have only added nodes as children of the root node Part of the power of XML is the

ability to express hierarchical data in a tree structure To demonstrate this capability, let ’ s add

the next node as a child of the attributed node You can accomplish this by passing the pointer

to the attributed node into the call to xmlNewChild :

// Create a node as a child of the attributed node

xmlNewChild (attributedNode, NULL, BAD_CAST “AttributedChild”,

BAD_CAST “Attributed Node Child Node”);

MakeXMLViewController.m

Instead of appending the new AttributedChild node to the rootNode , this code appends the

AttributedChild node to the attributedNode

Sometimes it may be more convenient to build the node and its associated text separately You can

do this and then add the node to the tree after you have populated it with text or other subnodes

A new node and its text content are created in this snippet Then, you add the text to the node and

append the node to the root node, as follows:

// You can also build nodes and text separately then and add them

// to the tree later

xmlNodePtr attachNode = xmlNewNode(NULL, BAD_CAST “AttachedNode”);

xmlNodePtr nodeText = xmlNewText(BAD_CAST “Attached Node Text”);

// Add the text to the node

xmlAddChild(attachNode, nodeText);

// Add the node to the root

xmlAddChild(rootNode, attachNode);

MakeXMLViewController.m

It is even possible to include XML comments using the xmlNewComment function:

Trang 9

// You can even include comments xmlNodePtr comment;

comment = xmlNewComment(BAD_CAST “This is an XML Comment”);

xmlAddChild(rootNode, comment);

MakeXMLViewController.m

Now that you have built the complete tree for the document, you have to tell the library to output the contents so that you can put the document into an NSString Because libxml is a C library, there is a function that outputs the document to a memory buffer You need to declare a memory buffer of type xmlChar* and an int variable to hold the length of the buffer Once you have these variables set up, you can call the xmlDocDumpFormatMemory function This function outputs the XML document to the buffer that you pass in and returns the size of the buffer in a reference variable that you also pass in to the function The last parameter to the xmlDocDumpFormatMemory function indicates that you would like space characters added to format the output Here is the code:

// Write the doc xmlChar *outputBuffer;

int bufferSize;

// You are responsible for freeing the buffer using xmlFree // Dump the document to a buffer

xmlDocDumpFormatMemory(doc, & outputBuffer, & bufferSize, 1);

MakeXMLViewController.m

Fortunately, because Objective - C is a superset of C, Apple has created plenty of helper functions for getting data from C types into Objective - C types For instance, the NSString class has a method called initWithBytes:length:encoding: that accepts a C char* buffer, a length, and an encoding type, and initializes an NSString with that data You use that function to create an NSString from your XML document dump:

// Create an NSString from the buffer NSString *xmlString = [[NSString alloc] initWithBytes:outputBuffer length:bufferSize encoding:NSUTF8StringEncoding];

Next, you log the XML document string to the console, put the string in the textView in the user interface, and release the NSString :

// Log the XML string that we created NSLog (@”output: \n%@”, xmlString);

// Display the text in the textview [self.textView setText:xmlString];

// Free the xml string [xmlString release];

Trang 10

Finally, you call some cleanup functions from libxml to free the memory that you allocated when

creating your XML document First, you free the output buffer that you just created with a call to

xmlFree Then, you free the memory used by the XML document with a call to xmlFreeDoc You

end with a call to xmlCleanupParser because you are completely fi nished with the XML parser:

// Clean up

// Free the output buffer

xmlFree(outputBuffer);

// Release all of the structures in the document including the tree

xmlFreeDoc(doc);

xmlCleanupParser();

MakeXMLViewController.m

You should be careful to ensure that you are not using the XML parser anywhere else in your

code before calling xmlCleanupParser This method cleans up any remaining memory that the

XML parser allocated, including global memory You should only call this function when your

application is completely fi nished using libxml and all documents that you have created with the

library

Listing 10 - 1 shows the entire generateXML method call

LISTING 10 - 1: The generateXML method

- (void) generateXML {

xmlDocPtr doc;

xmlNodePtr rootNode;

// Create a new xml document

doc = xmlNewDoc(BAD_CAST “1.0”);

// Create the root node

rootNode = xmlNewNode(NULL, BAD_CAST “rootNode”);

// Set the root of the document

xmlDocSetRootElement(doc, rootNode);

// Create a new child off the root

xmlNewChild(rootNode, NULL, BAD_CAST “ChildNode1”,

BAD_CAST “Child Node Content”);

// Add a node with attributes

xmlNodePtr attributedNode;

attributedNode = xmlNewChild (rootNode, NULL,

BAD_CAST “AttributedNode”, NULL);

xmlNewProp(attributedNode, BAD_CAST “attribute1”, BAD_CAST “First”);

xmlNewProp(attributedNode, BAD_CAST “attribute2”, BAD_CAST “Second”);

Ngày đăng: 04/07/2014, 21:20

w