본문 바로가기

AndroidStudio

<버스 공공데이터> XPath를 이용한 xml parser

노선정보 공공데이터를 이용해 처음으로 파싱을 해보았다.

 

주요 문제점은 공공데이터의 list 다음 부분의 항목명을 계속 리턴하지 않았다.  로그캣을 이용해 확인해보니 인증키가 먹통이 되어 service key is not registered error를 계속 리턴하는 상황이었다. 처음에는 routeList까지는 리턴하기에 서비스키의 문제일 거라고는 전혀 생각하지 못했는데 아무리 생각해도 더이상의 방안은 없는 것 같아 구글링을 열심히 했다.

대부분의 사이트나 QnA에서는 URL.encode를 통해 UTF-8 charset으로 변환해 서비스키를 넣어야 한다고 하기에 그 부분을 거듭 봤지만 문제는 전혀 없었다.

 

그렇게 몇 시간이 흐르고....

 

어느 한 개발자 분이 UTF-8 인코딩을 없애니 정상작동이 되었다는 말을 듣고, 인코딩을 해제해보았다.

그러자 정상작동..... 정상작동!!!!!!!!!!!!!!!!!!!!

이틀을 하루종일 값을 꺼내오는데 시간을 들였는데 청개구리 마냥 다른 많은 답안들을 무시하니 이게 된다고????

기타 REST(URI)의 다른 입력값도 마찬가지 에러를 내놓았는데 이 부분도 오히려 charset을 해제하니 해결이 됐다.

 

해답을 어려운데서만 찾다가 그렇게 간단한 곳에서 찾아내니 이렇게 허망할 수가.... 따흐흑....

<RFC30>
<code type="string">000</code>
<msg type="string">OK</msg>
<routeList class="array">
<list class="object">
<brtBusnum type="string">15</brtBusnum>
<brtClass type="string">0</brtClass>
<brtDirection type="string">1</brtDirection>
<brtEname type="string">전주비전대학</brtEname>
<brtFirsttime type="string">0605</brtFirsttime>
<brtId type="string">105</brtId>
<brtIlreginterval type="string">N</brtIlreginterval>
<brtLasttime type="string">2230</brtLasttime>
<brtLength type="string">31600</brtLength>
<brtMaxinterval type="string">0</brtMaxinterval>
<brtMininterval type="string">17</brtMininterval>
<brtNeedtime type="string">0</brtNeedtime>
<brtSname type="string">평화동종점</brtSname>
<brtStdid type="string">305200570</brtStdid>
<brtSubid type="string">0</brtSubid>
</list>
</routeList>
</RFC30>

 

package com.example.publictransitapplication;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserFactory;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;

public class MainActivity extends AppCompatActivity {

    //노선정보 서비스 인증키
    private static String key;
    private static String endPoint =
            "http://openapi.jeonju.go.kr/jeonjubus/openApi/traffic";

    String data;

    //UI필드 선언
    EditText editText;
    Button btnSearch;
    TextView textView;

    //필드 선언
    private String busNum;
    BufferedReader br;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        editText = (EditText) findViewById(R.id.editText);
        textView = (TextView) findViewById(R.id.textView);
    }

    public void mOnClick(View v){
        switch(v.getId()){
            case R.id.btnSearch:

                new Thread(new Runnable(){
                    @Override
                    public void run(){
                        try {
                            data = getXmlData();
                        } catch (UnsupportedEncodingException e) {
                            e.printStackTrace();
                        }

                        runOnUiThread(new Runnable(){
                        @Override
                        public void run(){
                            textView.setText(data);
                        }
                        });
                    }
                }).start();
                break;
        }
    }

    String getXmlData() throws UnsupportedEncodingException {
        StringBuilder stringBuffer = new StringBuilder();

        key = 인증키 //키 encode시 오류남.
        busNum = editText.getText().toString();
        //busNum = URLEncoder.encode(str, "UTF-8"); //encode시 오류남.
        Log.d("hoit", "key, busNum encoding 작업 완료");
        //노선정보항목조회
        StringBuilder urlBuilder =
                new StringBuilder("http://openapi.jeonju.go.kr/jeonjubus/openApi/traffic/bus_location1_common.do");
        urlBuilder.append("?" + URLEncoder.encode("ServiceKey","UTF-8") + "=" + key);
        urlBuilder.append("&" + URLEncoder.encode("brtId","UTF-8") + "=" + busNum);

        try {
            //DOM
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            factory.setNamespaceAware(true);
            DocumentBuilder docBuilder = factory.newDocumentBuilder();
            Document doc = null;

            //URL
            URL url = new URL(urlBuilder.toString());
            Log.d("hoit", "작업 위치의 URL 객체화 완료");
            HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
            Log.d("hoit",urlConnection.getResponseMessage());

            //응답 읽기
            br = new BufferedReader(new InputStreamReader(urlConnection.getInputStream(), "UTF-8"));
            String result = "";
            String line;
            while((line = br.readLine()) != null){
                result = result + line.trim();
                Log.d("hoit",result);
            }

            //xml 파싱
            InputSource is = new InputSource(new StringReader(result));
            doc = docBuilder.parse(is);
            XPathFactory xPathFactory = XPathFactory.newInstance();
            XPath xpath = xPathFactory.newXPath();
            XPathExpression xpe = xpath.compile("//routeList /list");
            NodeList nodeList = (NodeList) xpe.evaluate(doc, XPathConstants.NODESET);
            for(int i=0; i<nodeList.getLength(); i++){
                NodeList child = nodeList.item(i).getChildNodes();
                for(int j=0; j<child.getLength(); j++){
                    Node node = child.item(j);
                    stringBuffer.append("현재 노드 이름: " + node.getNodeName());
                    stringBuffer.append("\n");
                    stringBuffer.append("현재 노드 타입: " + node.getNodeType());
                    stringBuffer.append("\n");
                    stringBuffer.append("현재 노드 값: " + node.getTextContent());
                    stringBuffer.append("\n");
                    stringBuffer.append("현재 노드 네임스페이스: " + node.getPrefix());
                    stringBuffer.append("\n");
                    stringBuffer.append("현재 노드의 다음 노드: " + node.getNextSibling());
                    stringBuffer.append("\n\n");
                }
            }
        } catch(Exception e){
            Log.d("hoit", e.getMessage());
        }

        return stringBuffer.toString();
    }
}

읽어내는 데이터의 오류 여부를 확인하고 싶은 경우, 

//응답 읽기 부분에 Log.d를 설치해 응답하는 result 값을 로그캣으로 출력하도록 했다.

 

참고: XPath를 이용한 파싱은 

https://jeong-pro.tistory.com/144

 

자바 XML 파싱 라이브러리, XPath를 사용한 예제와 정확하게 element를 선택하는 방법을 알아보자

Java XML parse Library (=XPath) XPath : 자바에서 내장 패키지(javax.xml.xpath)로 제공하는 라이브러리로 XML형식의 웹문서, 파일, 문자열을 파싱하는데 사용한다. 요즘 누가 XML쓰나? JSON이랑 비교했을 때..

jeong-pro.tistory.com

이분의 글을 잘 참고했다.

'AndroidStudio' 카테고리의 다른 글

발생하는 예외 대처법  (0) 2020.06.16