iPhone App2011.09.21 19:33

일단 이전에 만든 XML은 진짜 기본적인 Parser인거 같다... 내가 실제로 XML Parser를 사용함으로써 수많게 유용하고 유연하게

바꾸었다. 어떤 XML이 들어와도 읽을수 있게(이건 오버인가?..)

일단 발전된 상황이라면 모든 XML을 Dictionary로 관리하기 때문에 유연하게 바뀌었다.

XML Parser에서 initWithContentsOfURL을 이용하면 네트워크가 안될시나 URL이 잘못되면 어떠한 예외를 던지지 못하고 멈추는 현상이 있어서

일단 나는 파싱하기전에 그 URL이 XML을 정상적으로 리턴하는지 검증하는것이 필요 하였다.

원리는 이렇다.

일단 XML받는부분을 Background에서 실행한다.

[self performSelector:@selector(getTime) withObject:nil];


그리고 아래 예제처럼하였다.


- (void) getTime

{

NSString *serverIP = [[NSUserDefaults standardUserDefaults] objectForKey:@"ServerIP"];

NSURL* url =[NSURL URLWithString:[[NSString stringWithFormat:TimeElementsURL,serverIP]stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];

  

XmlManager* xmlManager= [[XmlManager alloc] init];

  

NSData* xmlData = [xmlManager GetXmlData:url isPostData:NO];

  

if(xmlData != nil) {

NSMutableDictionary *xmlDict= [xmlManager GetXmlDictDataByData:xmlData elementFile:@"TimeElements.plist"];

NSUserDefaults *timeData = [NSUserDefaults standardUserDefaults];

[timeData setObject:xmlDict forKey:@"TimeData"];

[timeData synchronize];

  

//시간 설정

currentTime = [[xmlDict objectForKey:@"TimeSec"] intValue];

int secs = currentTime % 60;

int mins = (currentTime % 3600) / 60;

int hours = currentTime / 3600;

timeRemainLabel.text = [NSString stringWithFormat:@"%.2d:%.2d:%.2d",hours, mins, secs];

  

}

else {

NSLog(@"time data 얻기 실패");

}

  

  

[xmlManager release];

}

소스는 거창하지만 실제로 보면

NSData* xmlData = [xmlManager GetXmlData:url isPostData:NO];  


이부분을 보면 된다. 내가 구현한 xmlManager는 파일첨부 할것이다.

저렇게 일단 XML을 파싱하기전에 파싱데이터를 가져온다.

만약 저기서 data를 못받으면(data == nil) 실패했다고 말해준다.

이것이 바로 검증인것이다.
실패했는지 성공했는지 결과를 알수 있다는것이다.

성공하면 이렇게 파싱한다.
NSMutableDictionary *xmlDict= [xmlManager GetXmlDictDataByData:xmlData elementFile:@"TimeElements.plist"];
 
그리고 elementFile은 plist를 만들어서



 
이렇게 만든다. 

NSManager에 GetXmlData 소스는 이렇다.
-(NSData*)GetXmlData:(NSURL*) url isPostData:(BOOL) isPost

{

    NSMutableURLRequest* feedRequest = [NSMutableURLRequest requestWithURL:url cachePolicy: NSURLRequestUseProtocolCachePolicy timeoutInterval:10];

    if(isPost == YES)

    {

        [feedRequest setHTTPMethod:@"POST"];

    }

    NSError* theError = nil;

NSData* xmlData = [NSURLConnection sendSynchronousRequest:feedRequest returningResponse:nil error:&theError];     

//    NSString *str = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];     //확인용    


    if(theError != nil || xmlData ==nil)

    {        

        //네트워크가 잘못됬거나 data 가져 오지 못했을 경우 nil return

        return nil

    }

    else

    {

        return xmlData;

    }


2011년 10월 12일  추가

NSXmlparser버그로 인해서 첫글자가 숫자일경우 짤려서 foundCharacters 두번 호출되는 버그가 있었다.

Xml에 들어온글중에 원래 xml의 element안에 있는글이

"
1004 story 들어오기. 나도 글을 쓰고 싶을 뿐이다 ㅠㅠ"

라는 글이였는데 
 

"에 들어오기. 나도 글을 쓰고 싶을 뿐이다 ㅠㅠ"

만 나오는것이였다 보니까 1004 story라는 글이 짤려 있었다.

그래서 보니 첫글자가 숫자가 들어가니 아래 로그 처럼 나뉘어서 두번에 걸쳐서 foundCharacters delegate가 호출 되었다.

 

2011-10-12 11:09:01.447 나도 작가다[1151:207] CmtContent

2011-10-12 11:09:01.447 나도 작가다[1151:207] 1004 story  , CmtContent

2011-10-12 11:09:01.448 나도 작가다[1151:207] count = 2, index= 0 

2011-10-12 11:09:01.448 나도 작가다[1151:207] 들어오기. 나도 글을 쓰고 싶을 뿐이다 ㅠㅠ , CmtContent

그래서 아래처럼 수정하였다.

- (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];                               

        

        NSMutableDictionary *dataInfoDict = (NSMutableDictionary*)parentElement;        

        

        //NSXmlparser버그로 인해서 첫글자가 숫자일경우 짤려서 foundCharacters 두번 호출된다.

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

        {

            if([[dataInfoDict allKeys] containsObject:element])

            {                

                NSString* previousString = (NSString*)[dataInfoDict objectForKey:element];

                trimmedValue =[previousString stringByAppendingString:trimmedValue]; 

            }

        }

        

        [dataInfoDict setObject:trimmedValue forKey:element]; 

    }    

}


요약하자면 이전에 dictionary를 검색해서 이미 값이 있으면 이전 값과 현재 값을 합쳐서
저장한다.

만약 이전값이 없으면 무시되니깐 정상적으로 작동한다.
 
 
최신 xmlParser는 올려놓겠다

 

Posted by 동동(이재동)