본문 바로가기

iPhone

APXML 를 사용 해 보세요.

iPhone SDK 에서 제공하는 Xml Parsing 을 사용하다 보면..' 좀 더 편한 방법으로 파싱 할 수 있는 라이브러리를 만들어 볼까?' 싶은 생각이 듭니다. (이럴 때 많으시죠?) 그렇다고, 맘만 먹으면 뚝딱 해 내느냐.. 그건 개인 재량마다 차이가 있겠지만, 아무래도 쉬이 되는 일은 아니죠. 시간 투자를 해야 하니깐요 :)

이럴 때, 저는 OpenSource 를 찾아봅니다. SAX 방식인지, DOM 방식인지.. 고려할 사항이 많겠지만, 저는 사용하기 쉬운 쪽에 중점을 두고 찾아봤어요. 그 중에 아는 지인께서 소개 해 준 APXML 을 서치 해 봤는데요, 다른 Xml Parser 보다 사용하기가 정말 쉽더라고요. 특히, 닷넷 플랫폼에 익숙한 저한텐 더욱 더 사용하기 편했습니다.

(참고로 인터넷에서 검색 해 보시면 정말 많은 OpenSource 가 존재하는데요, 많은 분들이 쓰고 계시는 Xml Parser 를 비교 해 놓은 사이트가 있더군요.  APXML 이외에 다른 Xml Parser 도 한 번 검토 해 보시라고, 링크 남깁니다. )

그럼 APXML 이 왜 편한가! 또한, 닷넷 플랫폼에 익숙하신 분들에게는 더욱 더 사용하기 편한 이유가 무엇인가.. 를 살펴 볼까요?

우선, APXML 을 다운 하세요.

APXML 은 APDocument, APElement 라는 클래스를 정의 합니다.
APDocument, APElement.. 닷넷 플랫폼을 경험하신 분들이라면.. XDocument, XElement 를 기억하시죠? 맞습니다. APDocument와 APElement 는 그대로, XDocument, XElement 와 같은 역할을 하는 클래스입니다. 정말 흡사하게 구현 해 놓았더군요. 덕분에, XML Parsing 을 구현하는데 있어, 마치 C#으로 구현하는 것과 같은 방법으로 할 수 있다는 장점이 있어요. (기존에 C#에서 XML Parsing 을 해 보신 분이라면)

APXML 을 이용해 Xml Parsing
1. iPhone Project에 APXML Source 추가
2. Xml Parsing 할 곳에서 APXML 라이브러리를 사용하기 위한 해더 추가
3. xml 을 NSString 객체에 할당.
4. APDocument 객체 생성. (1. 에서 할당 된 NSString 사용)
5. XML 구문에 맞게 APElement 를 조사하며 파싱
- 끝 -

너무 쉽죠? :)
순서대로 코드를 한 번 리뷰하시면, 충분 하실 것 같네요 :) 다음 코드를 참고 하세요~
1. iPhone Project 에 APXML Source 추가
  APXML 을 다운 하신 뒤, iPhone Project에 소스를 통째로 추가 해 주시면 됩니다.

2. Xml Parsing 할 곳에서 APXML 라이브러리를 사용하기 위한 header 추가
  #include "APXML.h" 만 코드에 추가 하시면 됩니다.
  (APXML.h 에서는 APDocument.h , APElement.h, APAttribute.h 해더가 추가 되 있거든요)

3. xml 을 NSString 객체에 할당.
 보통 웹에서 다운하시니, 코드를 웹에서 다운 받는 형태로 작성 해 볼께요.

NSString* xmlStr = [NSString stringWithContentsOfURL:[NSURL URLWithString:xmlUrl]

encoding:NSUTF8StringEncoding error:nil];

  xmlUrl 만 입력하시면 되겠네요.

4. APDocument 객체 생성. (3. 에서 할당 된 NSString 사용)

APDocument* doc = [APDocument documentWithXMLString:xmlStr];

 APDocument 는 3번에서 할당 된 NSString 객체만 파라미터로 넘겨주시면 됩니다.

5. XML 구문에 맞게 APElement 를 조사하며 파싱
  - APElement 에서는 rootElement 라는 프로퍼티가 있어요. XML 문서의 Root 를 가져오게 됩니다.  반환 형식은 APElement 이고, 하위 엘리먼트들도 모두 포함하고 있습니다.  이 Root 를 베이스로, 하위 엘리먼트 요소를 XML 구문에 맞게 파싱하시면 되는 것이죠 :)

APElement* rootElement = [doc rootElement];


  - 또한, APElement 는 하위 엘리먼트 요소들을 childElements라는 프로퍼티를 통해 배열로 가지고 있습니다. 중요한 점은, 배열의 요소 각각 또한 APElement 라는 점입니다. 그래서, depth 가 아무리 깊더라도 파싱이 난해하지 않고 편하다는 것이죠 :)

NSArray* childElements = [rootElement childElements];

for(APElement* child in childElements) {

NSArray* memberElements = [child childElements];

MEMBER* obj = [[MEMBER alloc] init];

for(APElement* member in memberElements) {

// obj 객체 프로퍼티 할당

}

[members addObject:obj];

[obj release];

}


  - 마지막으로, 위 코드에서 주석처리 된 obj 객체 프로퍼티 할당 부분에 대해서 덧하자면.. (사실 이게 핵심이겠네요) Element 의 값을 가져오는 방식은 다음과 같습니다.

// 엘리먼트명을 가져옵니다.

// 예) <MEMBER id="1"> 에서 값 : MEMBER

[child name]

 

// 엘리먼트 내 Attribute의 값을 가져옵니다.

// 예) <MEMBER id="1"> 에서 값 : 1

[child valueForAttributeNamed:@"id"]

 

// 엘리먼트의 값을 가져옵니다. 

// 예) <PHONE>010-0505-7575</PHONE> 에서 값 : 010-0505-7575

[child value]


정말 쉽죠? 또한, XDocument 와 XElement 와도 매우 흡사합니다. 
XDocument - APDocument, 
XDocument.Root - [APDocument rootElement],
XElement - APElement
XElement.Elements - [APElement childElements]
위 4가지 사항이 너무나 매치가 되기 때문에 XDocument, XElement 를 사용해 보신 분들께는 더욱 더 추천 드리고 싶네요 ^^

  • 따발총 2010.08.04 15:15

    예제 소스 첨부해주시면 안될까요
    너무 감사합니다

  • Favicon of http://twitter.com/@gamell48 가가멜 2010.08.16 22:53

    제가 파싱에 대한 개념이 별로 없어서 100% 이해하지는 못했지만 조금만 연구하면 어떻게 쓸수 있을지 알것같습니다...
    감은 조금 오니 다행... ㅠ.ㅜ... 감사합니다...^^*

  • Favicon of http://twitter.com/@gamell48 가가멜 2010.08.17 20:09

    예제의 URL 를 바꿔서 트위터 rss 를 가져와봤는데... 엄청 빠르고 좋네요... ^^*
    그런데 파싱해온 title 문자열이 뜰때 "gamell48: I'm testing Parser..." 요런식으로 뜨는데...
    첫글자 9글자를 잘라버리고 나머지 내용만 테이블에 표현하고 싶은데... 뭘 어디서 어떻게 편집해야 할지 헷깔리네요... ㅠ.ㅜ

    • Favicon of https://mingu.kr Min-gu, Kim 2010.08.18 09:43 신고

      URL만 바꿔서 파싱이 됐다니 놀랍네요;
      Daum RSS와 트위터 RSS가 XML 구조가 비슷했던가 봅니다.

      트위터 RSS의 XML구조를 정확히 분석 하신 후에 XML Parsing 부분을 직접 구현 해 보세요. (샘플은 참고하시고^^)

  • 안되용 2010.08.18 18:00

    샘플에서 해당 URL만 바꿔서 해봤는데 안되요..;;

    실행 시키면 그냥 하얗 화면만 나와요;;

    • Favicon of https://mingu.kr Min-gu, Kim 2010.08.18 18:15 신고

      샘플은 DAUM RSS의 구조를 분석해서 파싱을 구현 한 것입니다.
      원하시는 URL 에서 Response 되는 XML 구조를 파악하시고, 그에 맞춰 파싱을 구현하셔야 됩니다.

  • 문의드립니다. 2010.09.06 10:53

    위에서 APXML 소스를 다운을 받아서 아이폰 프로젝트에 소스를 통째로 다 넣으라고 하셨는데요;;

    이부분이 이해가 안됩니다..;; 음..새로운 프로젝트를 만들고 그위에 드래그 해서 놓으라는 말씀인가요?

    • Favicon of https://mingu.kr Min-gu, Kim 2010.09.06 14:50 신고

      APXML 소스를 다운 받으시면, APAttribute.h, APAttribute.m, APDocument.h, APDocument.m, APElement.h, APElement.m, APXML.h 총 7개의 파일이 있을 겁니다.

      새 프로젝트를 만드신 후에, 위 7개 파일을 프로젝트에 드래그 앤 드랍으로 끌어서 Add 해 주시면 됩니다.

      (샘플을 다운 하셔서 참조 해 주세요.)

  • dhdh 2010.09.28 00:29

    한글이 입력되있으면 파싱이 안되는데 방법이없나요?
    서버에서 파싱한 자료로 로컬에서 자료 비교해서 다시 저장할려는데 방법좀 가르쳐주세요

    • Favicon of https://mingu.kr Min-gu, Kim 2010.09.28 09:34 신고

      한글 입력이 되 있다 하더라도, 인코딩에 맞춰 잘 파싱하면 아무 문제 없이 됩니다.

      혹, 엘리먼트 값에 XML 구문을 흐트리는 값이 있다면 CDATA 로 묶어서 보내시면 되고요 :)

  • dhdh 2010.09.28 14:36

    아직초보라 이해가 잘안되네요 답변감사합니다
    CDATA로 묶는게 어떤건가요?
    그리고 xml 두 파일을 비교하면서 다른값만 저장할건데 파싱해서 해야하나요?

    • Favicon of https://mingu.kr Min-gu, Kim 2010.09.29 09:43 신고

      <![CDATA[ ...... ]]> 이런식으로 엘리먼트 값을 묶는 겁니다. 이렇게 CDATA로 엘리먼트 값을 묶으면, & 등의 XML파서 구문을 흐트리는 문자열을 포함 해도 잘 전달이 되죠.

      XML 두 파일을 비교하면서 다른 값만 저장하려고 하신다면, 두 파일 모두 파싱한 뒤에 객체나 배열 등으로 만들어서 비교하시는게 맞을 것 같네요~

  • 애기아빠 2010.10.22 11:35

    혹시 XML파일이 아니라 XML string으로 받아서 하면 안되나요?
    웹서버에 접속해서 XML을 string으로 받아오는데 NSXML이 안되서 이걸로도 해보니까 안되네요..
    어떤 차이가 있는건지..
    뭔가 제가 잘 모르고 하는거 같기도 하고. 아직 한참 초보라..
    받아와서 string으로 변환해서 찍어보면 잘 나오는데 파싱을 하면 안되네요..
    어떤분은 안되서 파일로 만든다음에 다시 읽어서 했다고 하는데.. 그건 좀 아니다 싶거든요..
    좀 부탁드립니다.

    • Favicon of https://mingu.kr Min-gu, Kim 2010.10.22 12:52 신고

      Response 되는 텍스트가 XML 형식이라면 올바르게 파싱이 잘 됩니다.

      그리고 APXML도 내부적으로 NSXML 의 프로토콜을 사용합니다.

      애기아빠님의 경우에는 XML 구문이 올바르게 되 있는지 확인 해 보시는게 우선일 것 같네요.

      우선 Debug로 Response 되는 텍스트를 디버그 콘솔창에 찍으신 뒤에, 그대로 긁어서 xml 파일로 저장하시고, 브라우저에서 한 번 띄워보세요. XML 구문이 잘 못 된 거라면 브라우저 상에서 에러를 잡기가 수월할 겁니다 :)

      또, 보기엔 XML구문이 올바른 것 같아도 안 되는 경우가 있습니다. 예를 들어, 첫행(xml 해더 부분)이 whitespace인 경우에도 파싱이 되지 않습니다.

  • 애기아빠 2010.10.22 14:11

    아 정말 감사합니다.
    XML string이 잘못됐다는 생각은 해보질 못했습니다.
    전 xml data만 맞으면 되는줄 알았지 첫행이 whitespace이면 안되는 줄은 몰랐읍니다.
    그런데 웹서버에서는 공백이 하나도 들어가지 않는데 아이폰에서 받으면 xml 앞뒤로 공백이 엄청 들어갑니다.
    뭔가 이유가 있는건지 NSData로 받은것을 NSString으로 변환하면서 그런건지 모르겠네요..
    공백을 제거하면 되겠지만 혹시 이유를 알 수 있을까요?
    정말 빠른답변 감사드리고요.. 커피라도 한잔 대접해드리고 싶은 심정이네요.. ^^;

    • Favicon of https://mingu.kr Min-gu, Kim 2010.10.22 14:22 신고

      저의 경우엔 Response 되는 XML이 whitespace가 들어와서 서버단에서 공백을 없애줬어요. NSString 으로 변환하면서 공백이 생긴 경우는 못 본 것 같아요 :)

      앞뒤로 Trimming 할 수 있는 메소드가 있으니 그거라도 한 번 참고 해 보세요 :)

      [NSStringObject stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]

      감사하다니요, 저도 감사합니다. 좋은 하루 되세요~

  • JoJo 2010.11.08 08:39

    질문이 있습니다. 위 APXML 소스를 가져다가 잘 손 봐서 쓰고 있는데요... 정말 감사합니다. :)

    질문은, xml 을 보내주는 서버의 응답이 느리거나 띄엄띄엄일 때 에러처리를 어떻게 해줘야 하는지 입니다.
    보여주신 APXML 소스의 내부에는 그런 부분이 없는거 같아서요... 힌트좀 부탁드립니다.

    • Favicon of https://mingu.kr Min-gu, Kim 2010.11.08 08:57 신고

      APXML의 역할은, 이미 Response 된 텍스트를 파싱하는 부분이라고 생각하시고, 말씀하신 xml 을 Request한 뒤 Response 해 주는 서버의 응답이 느리거나 띄엄띄엄 한 부분의 에러처리는 Request 하실 때 처리를 하시면 될 것 같습니다.

      본문 3. 의 내용이 바로 서버에 Request한 뒤 Response 되는 텍스트를 NSString 객체에 담는 부분입니다.
      NSString* xmlStr = [NSString stringWithContentsOfURL:[NSURL URLWithString:xmlUrl] encoding:NSUTF8StringEncoding error:nil]

      그리고는 본문4.에서 APXML 소스를 사용하여 본문3.에서 할당 된 NSString 객체로 APDocument를 만드는 것이죠.

      그러므로, 3번에서 에러처리를 하시면 되는데요. NSString 의 stringWithContentsOfURL: encoding: error: 에서 마지막 인자인 error 가 NSError 객체입니다. 메소드 실행 후 에러가 발생 한다면 NSError 객체가 반환 되겠죠. 이 NSError 객체를 검사 해 보시면 될 것 같습니다 :)

    • JoJo 2010.11.08 10:02

      오 감사합니다. 제가 찾고자 했던 정확히 그 부분을 집어주셨습니다.
      아직 제 능력이 부족해서 좀 어려움이 있습니다.
      그부분을 어떻게 검사하는지 조금만 더 힌트 주실수 있으신가요

    • Favicon of https://mingu.kr Min-gu, Kim 2010.11.08 10:27 신고

      http://developer.apple.com/library/ios/#documentation/Cocoa/Reference/Foundation/Miscellaneous/Foundation_Constants/Reference/reference.html%23//apple_ref/doc/uid/TP40003793

      위 링크 가보시면, NSError Code 들이 정의 되 있을겁니다. NSError 가 nil이 아니고, code 번호가 미리 정의 된 에러와 일치 할 경우 메시지를 띄워주던가 하는 방법으로 처리 하시면 되겠죠. 알맞는 에러 Code는 위 링크에서 한 번 찾아 보세요 ^^

      조금 밑으로 스크롤 내려 보시면, URL Loading System Error Codes 라는 부분이 있네요. 거기서 찾아보심 될 것 같아요.

    • JoJo 2010.11.08 15:00

      알려주신대로 코드를 확인해보니 제가 원하는것과 비슷하게 구현이 되는것 같습니다. 감사드립니다. ^^


      헌데, 이런 방식은 에러가 반환될 때까지 시간이 너무 오래걸리네요

      현재 wifi 신호는 잡혀있고 실제로 신호가 미약해서 통신이 원활하지 않을 때 에러처리를 구현해야 하는데;;

      이게 영 잘 안되네요... 혹시 다른 좋은 방법을 아시면 조언 부탁드립니다. ^^

    • Favicon of https://mingu.kr Min-gu, Kim 2010.11.09 10:50 신고

      음.. 신호가 미약할 경우엔 어떤 방법이라도 리턴이 느릴 것 같아요.
      Request 를 시작할때 타이머를 돌려서 지정 시간 내 Response 되거나 에러가 생기거나 하지 않으면 메시지를 띄워주거나 하는 방법으로 해야되지 않을까 싶네요 :)

  • 2011.03.30 10:02

    비밀댓글입니다

    • Favicon of https://mingu.kr Min-gu, Kim 2011.03.30 10:29 신고

      APXML 로는 힘들 것 같군요.

      hpple 라는 XML/HTML parser 오픈소스가 있어요. hpple를 한번 사용 해 보세요~

      https://github.com/topfunky/hpple