Flutter

회기역 지하철 시간 타이머 앱 - 2 (xml parse)

HAHAKO 2023. 5. 3. 16:24

https://data.seoul.go.kr/dataList/OA-12764/F/1/datasetView.do

 

열린데이터광장 메인

데이터분류,데이터검색,데이터활용

data.seoul.go.kr

 

여기서 API 인증키를 신청해서 받았고, 이제 서버통신만 하면되는줄 알았는데,,

여기서 제공하는 데이터가 xml 형식이더라...

json이면 편할텐데........

flutter로 xml 파싱하는 방법에 대해서 공부했다.

 

https://www.youtube.com/watch?v=J9b13J8-osM&list=PLgRxBCVPaZ_269IOayEny-_aq0LUQ2fGl&index=2 

자료는 이 영상을 참고했다.

 

우선 데이터 형식을 긁어서 어떤 형식인지 확인해야한다.

<realtimeStationArrival>
<RESULT>
<code>INFO-000</code>
<developerMessage/>
<link/>
<message>정상 처리되었습니다.</message>
<status>200</status>
<total>13</total>
</RESULT>
<row>
<rowNum>1</rowNum>
<selectedCount>3</selectedCount>
<totalCount>13</totalCount>
<subwayId>1001</subwayId>
<updnLine>상행</updnLine>
<trainLineNm>소요산행 - 외대앞방면</trainLineNm>
<statnFid>1001000124</statnFid>
<statnTid>1001000122</statnTid>
<statnId>1001000123</statnId>
<statnNm>회기</statnNm>
<ordkey>01000소요산0</ordkey>
<subwayList>1001,1063</subwayList>
<statnList>1001000123,1063075118</statnList>
<barvlDt>0</barvlDt>
<btrainNo>0112</btrainNo>
<bstatnId>190</bstatnId>
<bstatnNm>소요산</bstatnNm>
<recptnDt>2023-05-03 15:47:33</recptnDt>
<arvlMsg2>회기 도착</arvlMsg2>
<arvlMsg3>회기</arvlMsg3>
<arvlCd>1</arvlCd>
</row>
<row>
<rowNum>2</rowNum>
<selectedCount>3</selectedCount>
<totalCount>13</totalCount>
<subwayId>1063</subwayId>
<updnLine>상행</updnLine>
<trainLineNm>문산행 - 청량리방면</trainLineNm>
<statnFid>1063075119</statnFid>
<statnTid>1063075117</statnTid>
<statnId>1063075118</statnId>
<statnNm>회기</statnNm>
<ordkey>01002문산0</ordkey>
<subwayList>1001,1063,1067</subwayList>
<statnList>1001000123,1063075118</statnList>
<barvlDt>0</barvlDt>
<btrainNo>5092</btrainNo>
<bstatnId>234</bstatnId>
<bstatnNm>문산</bstatnNm>
<recptnDt>2023-05-03 15:47:38</recptnDt>
<arvlMsg2>[2]번째 전역 (상봉)</arvlMsg2>
<arvlMsg3>상봉</arvlMsg3>
<arvlCd>99</arvlCd>
</row>
<row>
<rowNum>3</rowNum>
<selectedCount>3</selectedCount>
<totalCount>13</totalCount>
<subwayId>1067</subwayId>
<updnLine>상행</updnLine>
<trainLineNm>청량리행 - 청량리방면</trainLineNm>
<statnFid>1067080118</statnFid>
<statnTid>1067080116</statnTid>
<statnId>1067080117</statnId>
<statnNm>회기</statnNm>
<ordkey>01010청량리0</ordkey>
<subwayList>1001,1063,1067</subwayList>
<statnList>1001000123,1063075118,1067080117</statnList>
<barvlDt>0</barvlDt>
<btrainNo>8060</btrainNo>
<bstatnId>166</bstatnId>
<bstatnNm>청량리</bstatnNm>
<recptnDt>2023-05-03 15:48:27</recptnDt>
<arvlMsg2>[10]번째 전역 (평내호평)</arvlMsg2>
<arvlMsg3>평내호평</arvlMsg3>
<arvlCd>99</arvlCd>
</row>
</realtimeStationArrival>

길다..

쨌든 이 데이터를 처리를 해야되는데 row로 나뉘어져 있는걸 확인할 수 있다.

일단 이 데이터를 담을 class를 선언해준다

 

class Subway {
  String? rowNum;
  String? selectedCount;
  String? totalCount;
  String? subwayId;
  String? updnLine;
  String? trainLineNm;
  String? statnFid;
  String? statnTid;
  String? statnId;
  String? statnNm;
  String? ordkey;
  String? subwayList;
  String? statnList;
  String? barvlDt;
  String? btrainNo;
  String? bstatnId;
  String? recptnDt;
  String? arvlMsg2;
  String? arvlMsg3;
  String? arvlCd;

  Subway({
      this.rowNum,
      this.selectedCount,
      this.totalCount,
      this.subwayId,
      this.updnLine,
      this.trainLineNm,
      this.statnFid,
      this.statnTid,
      this.statnId,
      this.statnNm,
      this.ordkey,
      this.subwayList,
      this.statnList,
      this.barvlDt,
      this.btrainNo,
      this.bstatnId,
      this.recptnDt,
      this.arvlMsg2,
      this.arvlMsg3,
      this.arvlCd});
  }

항목이 너무많아도 어쩔 수 없다..

그 다음에는 깔끔한 코딩을 위해 xml을 집어넣는 클래스를 만들꺼다.

 

class PutElement{
  static String searchResult(XmlElement xml, String key){
    return xml.findAllElements(key).map((e) => e.text).isEmpty? "" :xml.findAllElements(key).map((e)=> e.text).first;
  }
}

이 함수를 만든 다음에

위에서 만든 Subway class 를 수정해주면 된다.

class Subway {
  String? rowNum;
  String? selectedCount;
  String? totalCount;
  String? subwayId;
  String? updnLine;
  String? trainLineNm;
  String? statnFid;
  String? statnTid;
  String? statnId;
  String? statnNm;
  String? ordkey;
  String? subwayList;
  String? statnList;
  String? barvlDt;
  String? btrainNo;
  String? bstatnId;
  String? recptnDt;
  String? arvlMsg2;
  String? arvlMsg3;
  String? arvlCd;

  Subway({
      this.rowNum,
      this.selectedCount,
      this.totalCount,
      this.subwayId,
      this.updnLine,
      this.trainLineNm,
      this.statnFid,
      this.statnTid,
      this.statnId,
      this.statnNm,
      this.ordkey,
      this.subwayList,
      this.statnList,
      this.barvlDt,
      this.btrainNo,
      this.bstatnId,
      this.recptnDt,
      this.arvlMsg2,
      this.arvlMsg3,
      this.arvlCd});

    factory Subway.fromXml(XmlElement xml){
      return Subway(
        rowNum : PutElement.searchResult(xml, 'rowNum'),
        selectedCount : PutElement.searchResult(xml, 'selectedCount'),
        totalCount : PutElement.searchResult(xml, 'totalCount'),
        subwayId : PutElement.searchResult(xml, 'subwayId'),
        updnLine : PutElement.searchResult(xml, 'updnLine'),
        trainLineNm : PutElement.searchResult(xml, 'trainLineNm'),
        statnFid : PutElement.searchResult(xml, 'statnFid'),
        statnTid : PutElement.searchResult(xml, 'statnTid'),
        statnId : PutElement.searchResult(xml, 'statnId'),
        statnNm : PutElement.searchResult(xml, 'statnNm'),
        ordkey : PutElement.searchResult(xml, 'ordkey'),
        subwayList : PutElement.searchResult(xml, 'subwayList'),
        statnList : PutElement.searchResult(xml, 'statnList'),
        barvlDt : PutElement.searchResult(xml, 'barvlDt'),
        btrainNo : PutElement.searchResult(xml, 'btrainNo'),
        bstatnId : PutElement.searchResult(xml, 'bstatnId'),
        recptnDt : PutElement.searchResult(xml, 'recptnDt'),
        arvlMsg2 : PutElement.searchResult(xml, 'arvlMsg2'),
        arvlMsg3 : PutElement.searchResult(xml, 'arvlMsg3'),
        arvlCd : PutElement.searchResult(xml, 'arvlCd'),
      );
    }
}

그러면 class에 xml 데이터를 parse하는 메소드는 만들었고 이제 직접적으로 main함수에서 parse를 해야한다.

임의로 데이터를 하나 긁어서 main에 넣어주고, 이를 넣어주는 main함수는

void main() {
  final bookshelfXml = '''<realtimeStationArrival>
<RESULT>
<code>INFO-000</code>
<developerMessage/>
<link/>
<message>정상 처리되었습니다.</message>
<status>200</status>
<total>13</total>
</RESULT>
<row>
<rowNum>1</rowNum>
<selectedCount>3</selectedCount>
<totalCount>13</totalCount>
<subwayId>1001</subwayId>
<updnLine>상행</updnLine>
<trainLineNm>소요산행 - 외대앞방면</trainLineNm>
<statnFid>1001000124</statnFid>
<statnTid>1001000122</statnTid>
<statnId>1001000123</statnId>
<statnNm>회기</statnNm>
<ordkey>01000소요산0</ordkey>
<subwayList>1001,1063</subwayList>
<statnList>1001000123,1063075118</statnList>
<barvlDt>0</barvlDt>
<btrainNo>0112</btrainNo>
<bstatnId>190</bstatnId>
<bstatnNm>소요산</bstatnNm>
<recptnDt>2023-05-03 15:47:33</recptnDt>
<arvlMsg2>회기 도착</arvlMsg2>
<arvlMsg3>회기</arvlMsg3>
<arvlCd>1</arvlCd>
</row>
<row>
<rowNum>2</rowNum>
<selectedCount>3</selectedCount>
<totalCount>13</totalCount>
<subwayId>1063</subwayId>
<updnLine>상행</updnLine>
<trainLineNm>문산행 - 청량리방면</trainLineNm>
<statnFid>1063075119</statnFid>
<statnTid>1063075117</statnTid>
<statnId>1063075118</statnId>
<statnNm>회기</statnNm>
<ordkey>01002문산0</ordkey>
<subwayList>1001,1063,1067</subwayList>
<statnList>1001000123,1063075118</statnList>
<barvlDt>0</barvlDt>
<btrainNo>5092</btrainNo>
<bstatnId>234</bstatnId>
<bstatnNm>문산</bstatnNm>
<recptnDt>2023-05-03 15:47:38</recptnDt>
<arvlMsg2>[2]번째 전역 (상봉)</arvlMsg2>
<arvlMsg3>상봉</arvlMsg3>
<arvlCd>99</arvlCd>
</row>
<row>
<rowNum>3</rowNum>
<selectedCount>3</selectedCount>
<totalCount>13</totalCount>
<subwayId>1067</subwayId>
<updnLine>상행</updnLine>
<trainLineNm>청량리행 - 청량리방면</trainLineNm>
<statnFid>1067080118</statnFid>
<statnTid>1067080116</statnTid>
<statnId>1067080117</statnId>
<statnNm>회기</statnNm>
<ordkey>01010청량리0</ordkey>
<subwayList>1001,1063,1067</subwayList>
<statnList>1001000123,1063075118,1067080117</statnList>
<barvlDt>0</barvlDt>
<btrainNo>8060</btrainNo>
<bstatnId>166</bstatnId>
<bstatnNm>청량리</bstatnNm>
<recptnDt>2023-05-03 15:48:27</recptnDt>
<arvlMsg2>[10]번째 전역 (평내호평)</arvlMsg2>
<arvlMsg3>평내호평</arvlMsg3>
<arvlCd>99</arvlCd>
</row>
</realtimeStationArrival>''';

  test('subway', () {
    final document = XmlDocument.parse(bookshelfXml);
    final items = document.findAllElements('row');
    var subway = <Subway>[];
    items.forEach((node) {
      subway.add(Subway.fromXml(node));
    });
    print(subway.length);
    subway.forEach((subway){
      print('${subway.trainLineNm} : ${subway.recptnDt}');
    });
  });
}

이렇다

test안의 내용이 중요한데, items를 xml파일을 row로 나눠주고

이를 각각 subway class 변수에 넣어준다.

그리고 print를 해보면 

Connecting to VM Service at http://127.0.0.1:56642/geY7XXBssi8=/ws
3
소요산행 - 외대앞방면 : 2023-05-03 15:47:33
문산행 - 청량리방면 : 2023-05-03 15:47:38
청량리행 - 청량리방면 : 2023-05-03 15:48:27
✓ subway
Exited

다음과 같이 결과가 나오는 것을 확인할 수 있다.

이제 parse는 끝났으니 http에서 데이터를 받아오는걸 해보면 된다.