본문 바로가기

Mobile/IOS

Objective-c xml 파싱, json 파싱

이 글은 공부한 내용을 정리하는 목적으로 적는 글이오니 건전한 비판은 언제든 환영합니다.

 

XML 파싱이란

대략적으로 xml 파싱이란 이런 모양으로 생겼다.

<weatherinfo>
  <local>
    <country>한국</country>
    <weather>비</weather>
    <temperature>20</temperature>
  </local>
  <local>
    <country>일본</country>
    <weather>맑음</weather>
    <temperature>19</temperature>
  </local>
  <local>
    <country>중국</country>
    <weather>눈</weather>
    <temperature>14</temperature>
  </local>
  <local>
    <country>스페인</country>
    <weather>우박</weather>
    <temperature>13</temperature>
  </local>
  <local>
    <country>미국</country>
    <weather>흐림</weather>
    <temperature>2</temperature>
  </local>
  <local>
    <country>영국</country>
    <weather>비</weather>
    <temperature>10</temperature>
  </local>
  <local>
    <country>프랑스</country>
    <weather>흐림</weather>
    <temperature>15</temperature>
  </local>
  <local>
    <country>브라질</country>
    <weather>흐림</weather>
    <temperature>35</temperature>
  </local>
  <local>
    <country>스위스</country>
    <weather>맑음</weather>
    <temperature>13</temperature>
  </local>
  <local>
    <country>덴마크</country>
    <weather>비</weather>
    <temperature>2</temperature>
  </local>
  <local>
    <country>스웨덴</country>
    <weather>눈</weather>
    <temperature>0</temperature>
  </local>
  <local>
    <country>네덜란드</country>
    <weather>비</weather>
    <temperature>12</temperature>
  </local>
  <local>
    <country>크로아티아</country>
    <weather>맑음</weather>
    <temperature>30</temperature>
  </local>
  <local>
    <country>필리핀</country>
    <weather>맑음</weather>
    <temperature>28</temperature>
  </local>
  <local>
    <country>독일</country>
    <weather>눈</weather>
    <temperature>3</temperature>
  </local>
  <local>
    <country>헝가리</country>
    <weather>비</weather>
    <temperature>13</temperature>
  </local>
  <local>
    <country>벨기에</country>
    <weather>흐림</weather>
    <temperature>8</temperature>
  </local>
  <local>
    <country>핀란드</country>
    <weather>우박</weather>
    <temperature>15</temperature>
  </local>
  <local>
    <country>이탈리아</country>
    <weather>맑음</weather>
    <temperature>23</temperature>
  </local>  
</weatherinfo>

위의 글들을 보면 weatherinfo 안에 local이 들어가 있고 local로 감싸는 부분에

country, weather, temperature가 들어가 있으며, 역서 파싱할 것은 local 안에 있는 부분을 꺼내줘야 한다.

 

파싱할 대상들을 꺼내보기

tableview를 만들고 그 안에 라벨과 이미지 뷰를 넣어놓았다.

그래서 cell파일을 따로 만들어 놓았다.

#import <UIKit/UIKit.h>

@interface WeatherCell : UITableViewCell

@property (strong, nonatomic) IBOutlet UIImageView *imgView;
@property (strong, nonatomic) IBOutlet UILabel *countryLabel;
@property (strong, nonatomic) IBOutlet UILabel *weatherLabel;
@property (strong, nonatomic) IBOutlet UILabel *temperatureLabel;

@end

그리고 ViewController.h 파일에서

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController<NSXMLParserDelegate, UITableViewDataSource,UITableViewDelegate>{
    NSXMLParser *parser;
    
    NSMutableArray *datalist;
    NSMutableDictionary *detailData;
    
    NSString *elementTemp;
    
    BOOL blank;

}


@end

XMLParserDelegate와 tableDataSource, tableDelegate를 받아주어서 처리하게 만들었다.

이제 ViewController.m 파일에서

#import "ViewController.h"
#import "WeatherCell.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    datalist = [[NSMutableArray alloc]init];
    parser = [[NSXMLParser alloc]initWithContentsOfURL:[NSURL URLWithString:@"https://raw.githubusercontent.com/ChoiJinYoung/iphonewithswift2/master/weather.xml"]];
    parser.delegate = self;
    [parser parse];
    
    NSLog(@"%@",datalist);
}

-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary<NSString *,NSString *> *)attributeDict{
    NSLog(@"didStartElement : %@", elementName);
    if ([elementName isEqualToString:@"local"]) {
        detailData = [[NSMutableDictionary alloc]init];
    }
    elementTemp = elementName;
    blank = YES;
}

-(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
    if (blank == YES && ![elementTemp isEqualToString:@"local"]) {
        NSLog(@"foundCharacters : %@",string);
        [detailData setObject:string forKey:elementTemp];
    }
    
}

-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{
    if ([elementName isEqualToString:@"local"]) {
        [datalist addObject:detailData];
    }
    NSLog(@"didEndElement : %@",elementName);
    blank = NO;
}


-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    return datalist.count;
}

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    WeatherCell *cell = [tableView dequeueReusableCellWithIdentifier:@"weather" forIndexPath:indexPath];
    NSDictionary *dicTemp = [datalist objectAtIndex:indexPath.row];
    
    cell.countryLabel.text = [dicTemp objectForKey:@"country"];
    NSString *weatherStr = [dicTemp objectForKey:@"weather"];
    cell.weatherLabel.text = weatherStr;
    cell.temperatureLabel.text = [dicTemp objectForKey:@"temperature"];
    
    if ([weatherStr isEqualToString:@"맑음"]) {
        cell.imgView.image = [UIImage imageNamed:@"sunny.png"];
    }else if ([weatherStr isEqualToString:@"비"]) {
        cell.imgView.image = [UIImage imageNamed:@"rainy.png"];
    }else if ([weatherStr isEqualToString:@"흐림"]) {
        cell.imgView.image = [UIImage imageNamed:@"cloudy.png"];
    }else if ([weatherStr isEqualToString:@"눈"]) {
        cell.imgView.image = [UIImage imageNamed:@"snow.png"];
    }else{
        cell.imgView.image = [UIImage imageNamed:@"blizzard.png"];
    }
    
    
    return cell;
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

위와 같이 만들어 주었는데 하나씩 분해하면 먼저 viewDidLoad에서는

- (void)viewDidLoad {
    [super viewDidLoad];
    
    datalist = [[NSMutableArray alloc]init];
    parser = [[NSXMLParser alloc]initWithContentsOfURL:[NSURL URLWithString:@"https://raw.githubusercontent.com/ChoiJinYoung/iphonewithswift2/master/weather.xml"]];
    parser.delegate = self;
    [parser parse];
    
    NSLog(@"%@",datalist);
}

먼저 datalist 객체를 생성하여 주고

parser를 xml데이터가 있는 http를 불러와서 delegate를 생성하여 준다.

-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary<NSString *,NSString *> *)attributeDict{
    NSLog(@"didStartElement : %@", elementName);
    if ([elementName isEqualToString:@"local"]) {
        detailData = [[NSMutableDictionary alloc]init];
    }
    elementTemp = elementName;
    blank = YES;
}

이 메소드에서는 먼저 <local>이라는 부분에서 안에 있는 것을 파싱해야 하기 때문에 먼저 local이라는 부분을 찾아주고 bool했던 부분을 YES로 바꿔준다.

-(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
    if (blank == YES && ![elementTemp isEqualToString:@"local"]) {
        NSLog(@"foundCharacters : %@",string);
        [detailData setObject:string forKey:elementTemp];
    }
    
}

그리고 local 안에 있는 것을 받아주고 넣어준다.

-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{
    if ([elementName isEqualToString:@"local"]) {
        [datalist addObject:detailData];
    }
    NSLog(@"didEndElement : %@",elementName);
    blank = NO;
}

마지막에 local이라고 하는 부분을 닫아준다.

 

이런식으로 parsing을 하면 된다

 

 

JSON 파싱

json도 xml파싱과 다르게 json으로 파싱할 수 있다.

{
  "weatherinfo": {
    "local": [
      {
        "country": "한국",
        "weather": "비",
        "temperature": "20"
      },
      {
        "country": "일본",
        "weather": "맑음",
        "temperature": "19"
      },
      {
        "country": "중국",
        "weather": "눈",
        "temperature": "14"
      },
      {
        "country": "스페인",
        "weather": "우박",
        "temperature": "13"
      },
      {
        "country": "미국",
        "weather": "흐림",
        "temperature": "2"
      },
      {
        "country": "영국",
        "weather": "비",
        "temperature": "10"
      },
      {
        "country": "프랑스",
        "weather": "흐림",
        "temperature": "15"
      },
      {
        "country": "브라질",
        "weather": "흐림",
        "temperature": "35"
      },
      {
        "country": "스위스",
        "weather": "맑음",
        "temperature": "13"
      },
      {
        "country": "덴마크",
        "weather": "비",
        "temperature": "2"
      },
      {
        "country": "스웨덴",
        "weather": "눈",
        "temperature": "0"
      },
      {
        "country": "네덜란드",
        "weather": "비",
        "temperature": "12"
      },
      {
        "country": "크로아티아",
        "weather": "맑음",
        "temperature": "30"
      },
      {
        "country": "필리핀",
        "weather": "맑음",
        "temperature": "28"
      },
      {
        "country": "독일",
        "weather": "눈",
        "temperature": "3"
      },
      {
        "country": "헝가리",
        "weather": "비",
        "temperature": "13"
      },
      {
        "country": "벨기에",
        "weather": "흐림",
        "temperature": "8"
      },
      {
        "country": "핀란드",
        "weather": "우박",
        "temperature": "15"
      },
      {
        "country": "이탈리아",
        "weather": "맑음",
        "temperature": "23"
      }
    ]
  }
}

오히려 json 파싱이 간편하게 작성할 수 있다.

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController<UITableViewDataSource,UITableViewDelegate>{
    
    NSDictionary *datalist;
    NSArray *local;
    
    
}


@end

viewController.h 파일에서 이렇게 만들어 주면서

viewController.m 파일에서는 



#import "ViewController.h"
#import "WeatherCell.h"
@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    NSError *err;
    datalist = [NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:@"https://raw.githubusercontent.com/ChoiJinYoung/iphonewithswift2/master/weather.json"]] options:NSJSONReadingAllowFragments error:&err];
    
    local = [[datalist objectForKey:@"weatherinfo"]objectForKey:@"local"];
    //NSLog(@"%@",local);

}

-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    return local.count;
}

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    WeatherCell *cell = [tableView dequeueReusableCellWithIdentifier:@"weather" forIndexPath:indexPath];
    NSDictionary *dicTemp = [local objectAtIndex:indexPath.row];
    
    cell.countryLabel.text = [dicTemp objectForKey:@"country"];
    NSString *weatherStr = [dicTemp objectForKey:@"weather"];
    cell.weatherLabel.text = weatherStr;
    cell.temperatureLabel.text = [dicTemp objectForKey:@"temperature"];
    
    if ([weatherStr isEqualToString:@"맑음"]) {
        cell.imgView.image = [UIImage imageNamed:@"sunny.png"];
    }else if ([weatherStr isEqualToString:@"비"]) {
        cell.imgView.image = [UIImage imageNamed:@"rainy.png"];
    }else if ([weatherStr isEqualToString:@"흐림"]) {
        cell.imgView.image = [UIImage imageNamed:@"cloudy.png"];
    }else if ([weatherStr isEqualToString:@"눈"]) {
        cell.imgView.image = [UIImage imageNamed:@"snow.png"];
    }else{
        cell.imgView.image = [UIImage imageNamed:@"blizzard.png"];
    }
    
    
    return cell;
}


- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

먼저 viewDidLoad에서

- (void)viewDidLoad {
    [super viewDidLoad];
    NSError *err;
    datalist = [NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:@"https://raw.githubusercontent.com/ChoiJinYoung/iphonewithswift2/master/weather.json"]] options:NSJSONReadingAllowFragments error:&err];
    
    local = [[datalist objectForKey:@"weatherinfo"]objectForKey:@"local"];
    //NSLog(@"%@",local);

}

json 데이터를 불러오고, weatherinfo에서 부분에서 local부분을 알려준다.

그리고 cell 부분에서

  WeatherCell *cell = [tableView dequeueReusableCellWithIdentifier:@"weather" forIndexPath:indexPath];
    NSDictionary *dicTemp = [local objectAtIndex:indexPath.row];
    
    cell.countryLabel.text = [dicTemp objectForKey:@"country"];
    NSString *weatherStr = [dicTemp objectForKey:@"weather"];
    cell.weatherLabel.text = weatherStr;
    cell.temperatureLabel.text = [dicTemp objectForKey:@"temperature"];

하나씩 key에 대해서 넣어준다.

'Mobile > IOS' 카테고리의 다른 글

iOS 커스텀 프레임워크 만들기  (0) 2023.09.18
ViewModel을 이용한 버튼 활성화 방법  (0) 2022.10.18
DispatchQueue  (0) 2022.07.01
CocoaPods 에러 Cannot find 'Auth' in scope  (0) 2022.05.04
UINavigationController  (0) 2022.04.26