iPhone App2011. 7. 21. 18:15

아이폰에서 XML을 파싱하기 위해서는 여러가지 방법이 있다.

라이브러리를 이용하는 방법(touchXML,KISSXML등등)과 NSXMLParser를 이용하는 방법이 있다.

아이폰이 아닌 그냥 맥개발에서는 NSXMLDocument가 있어서 편하게 쓸수 있다고 하는데 아무튼.. 그냥 힘든방법으로 가자.

c#으로 개발했을때는 시리얼라이즈가 있어서 클래스만 만들면 자동으로 xml을 만들고 파싱하고 아주 편했다.

하지만 Iphone Objective-C는........ㅠ.ㅠ

일단 WCF Rest서비스를 만들어서 DB내용을 XML로 동적으로 뿌려주기 위한 서비스를 만들었다.

그래서 파싱할 xml은 이렇다.

<ArrayOfHugeBoardInfo xmlns="http://schemas.datacontract.org/2004/07/HugeBoardService" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">

<HugeBoardInfo>

<Date>2010-02-02T00:00:00</Date>

<Description>hi</Description>

<Name>leejaedong</Name>

<Title>hi</Title>

<idx>1</idx>

</HugeBoardInfo>

일단 이 데이터를 저장할 클래스부터 만들자 C#이랑 비슷하다 여기까지는

@interface HugeBoardData : NSObject {

NSString* _idx;

NSString* _name;

NSString* _title;

NSString* _description;

NSString* _date;

}


@property(copy) NSString* idx;

@property(copy) NSString* name;

@property(copy) NSString* title;

@property(copy) NSString* description;

@property(copy) NSString* date;



@end

헤더에 이렇게 넣고

@implementation HugeBoardData

@synthesize idx= _idx;

@synthesize name= _name;

@synthesize title= _title;

@synthesize description = _description;

@synthesize date= _date;

@end


구현부에 구현을 하자

그다음 일단 Xml을 파싱을 전문적으로 하는 class를 만들자.

@interface HugeBoardXmlParser : NSObject<NSXMLParserDelegate> {

NSXMLParser *parser;

NSMutableArray *_hugeBoardDataArray;

NSMutableArray *_elementStack;

}


@property (nonatomic,retain) NSXMLParser *parser;

-(NSMutableArray*)parseContent: (NSURL*) url;

@end


이름을 HugeBoardXmlParser라 만들고 NSXMLParser 델리게이트를 참조 하였다.그리고 XML을 parser할 NSXMLParser와 xml파일의 모든 데이터를 저장할 hugeboardDataArray그리고 elementStack이라고 해서 한줄한줄 읽을때마다 저장해서 이게 어떤 노드에 있고 어떤값지를 임시로 저장해놓는 array이다

#import "HugeBoardXmlParser.h"

#import "HugeBoardData.h"


@implementation HugeBoardXmlParser


@synthesize parser;


- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict

{

if([elementName isEqualToString:@"HugeBoardInfo"] == TRUE)

{

HugeBoardData *data = [HugeBoardData new];

[_elementStack addObject:data];

}

else if([elementName isEqualToString:@"idx"] || [elementName isEqualToString:@"Name"] || [elementName isEqualToString:@"Title"] || [elementName isEqualToString:@"Description" ] || [elementName isEqualToString:@"Date"])

{

NSString *element = [NSString stringWithString:elementName];

[_elementStack addObject:element];

}

}


- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName

{

if([elementName isEqualToString:@"HugeBoardInfo"] == TRUE)

{

HugeBoardData *data =(HugeBoardData*)[_elementStack lastObject];

[_hugeBoardDataArray addObject:data];

[_elementStack removeLastObject];

}

else if([elementName isEqualToString:@"idx"] || [elementName isEqualToString:@"Name"] || [elementName isEqualToString:@"Title"] || [elementName isEqualToString:@"Description" ] || [elementName isEqualToString:@"Date"])

{

[_elementStack removeLastObject];

}

  

}


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

{

//trim 쓰자면 이걸 쓰고 안하면 그냥 string 값을 넣어도 된다.

NSCharacterSet *characterSet = [NSCharacterSet whitespaceAndNewlineCharacterSet];

NSString *trimmedValue = [string stringByTrimmingCharactersInSet:characterSet];

if([trimmedValue length] == 0)

{

return;

}

  

id element = [_elementStack lastObject];

if([element isKindOfClass:[NSString class]])

{

NSUInteger count = [_elementStack count];

NSUInteger index = count -2;

id parentElement = [_elementStack objectAtIndex:index];


HugeBoardData *data = (HugeBoardData*)parentElement;

if([element isEqualToString:@"idx"])

{

data.idx = trimmedValue;

}

else if([element isEqualToString:@"Name"])

{

data.name = trimmedValue;

}

else if([element isEqualToString:@"Title"])

{

data.title = trimmedValue;

}

else if([element isEqualToString:@"Description"])

{

data.description = trimmedValue;

}

else if([element isEqualToString:@"Date"])

{

data.date = trimmedValue;

}

}

}

-(NSMutableArray*) parseContent:(NSURL*) url

{

NSXMLParser* xmlParser = [[NSXMLParser alloc] initWithContentsOfURL:url];

[xmlParser setDelegate:self];

  

_hugeBoardDataArray =[NSMutableArray arrayWithCapacity:1024];

_elementStack = [NSMutableArray arrayWithCapacity:1024];

[xmlParser parse];

return _hugeBoardDataArray;

}

@end


이것은 구현부이다.
하나씩 알아보자.일단 파싱을 하기 위해서 필요한 3가지 델리게이트가 있다.첫째 이부분이다.

- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict

{

if([elementName isEqualToString:@"HugeBoardInfo"] == TRUE)

{

HugeBoardData *data = [HugeBoardData new];

[_elementStack addObject:data];

}

else if([elementName isEqualToString:@"idx"] || [elementName isEqualToString:@"Name"] || [elementName isEqualToString:@"Title"] || [elementName isEqualToString:@"Description" ] || [elementName isEqualToString:@"Date"])

{

NSString *element = [NSString stringWithString:elementName];

[_elementStack addObject:element];

}

}

초반 엘리먼트를 읽는다. 우린 이것을 일단 모두 저장한다. 어디에? elementStack에

그다음 호출되는것은 이거다

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

{

//trim 쓰자면 이걸 쓰고 안하면 그냥 string 값을 넣어도 된다.

NSCharacterSet *characterSet = [NSCharacterSet whitespaceAndNewlineCharacterSet];

NSString *trimmedValue = [string stringByTrimmingCharactersInSet:characterSet];

if([trimmedValue length] == 0)

{

return;

}

  

id element = [_elementStack lastObject];

if([element isKindOfClass:[NSString class]])

{

NSUInteger count = [_elementStack count];

NSUInteger index = count -2;

id parentElement = [_elementStack objectAtIndex:index];


HugeBoardData *data = (HugeBoardData*)parentElement;

if([element isEqualToString:@"idx"])

{

data.idx = trimmedValue;

}

else if([element isEqualToString:@"Name"])

{

data.name = trimmedValue;

}

else if([element isEqualToString:@"Title"])

{

data.title = trimmedValue;

}

else if([element isEqualToString:@"Description"])

{

data.description = trimmedValue;

}

else if([element isEqualToString:@"Date"])

{

data.date = trimmedValue;

}

}

}


실제 Value값을 여기서 얻을수가 있는데 아까 처음에 _elementStack에 저장했던 값을 분석해서 HugeBoardData라는 아까 만든 저장형 클래스에 담는다. trimmedValiue대신에 string을 넣어도 된다.
마지막으로 호출되는것은 이것이다.

- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName

{

if([elementName isEqualToString:@"HugeBoardInfo"] == TRUE)

{

HugeBoardData *data =(HugeBoardData*)[_elementStack lastObject];

[_hugeBoardDataArray addObject:data];

[_elementStack removeLastObject];

}

else if([elementName isEqualToString:@"idx"] || [elementName isEqualToString:@"Name"] || [elementName isEqualToString:@"Title"] || [elementName isEqualToString:@"Description" ] || [elementName isEqualToString:@"Date"])

{

[_elementStack removeLastObject];

}

}


여기서하는것은 아까 foundCharacters에서 저장한 HugeBoardData를 _HugeBoardDataArray에 저장한다. 여기까지가 한텀이 끝난것이다. 이제 아마 같은 방식으로 계속 한텀한텀씩 저장해서 모든 xml데이터를 _hugeBoardDataArray에 저장할것이다.한텀이 끝났으면 다음텀을 위해서 _elementStack을 삭제한다. 이렇게 3개의 델리게이트 메소드가 계속 호출되면서 반복된다.
이제 이 모든기능을 작동되게 만드는 외부에서 호출 당하는 메소드를 보자

-(NSMutableArray*) parseContent:(NSURL*) url

{

NSXMLParser* xmlParser = [[NSXMLParser alloc] initWithContentsOfURL:url];

[xmlParser setDelegate:self];

  

_hugeBoardDataArray =[NSMutableArray arrayWithCapacity:1024];

_elementStack = [NSMutableArray arrayWithCapacity:1024];

[xmlParser parse];

return _hugeBoardDataArray;

}


여기보면 xmlParser를 작동되게 하고 모든 xmlData를 포함하는 _hugeBoardDataArray를 리턴한다.
그래서 외부에서 사용할려면

NSURL *XMLURL =[NSURL URLWithString:@"http://192.168.10.3:9090/hugeboardService/getboardinfo"];

HugeBoardXmlParser* parser = [[HugeBoardXmlParser alloc] init];

NSMutableArray *hugeboardArr = [parser parseContent:XMLURL];


이렇게 하면 된다. 그럼 hugeBoardArr에 모든 데이터가 저장될것이다.



참고 한 사이트 : http://kiipos.delimount.net/1084

Posted by 동동(이재동)