OpenAPI_그중 xml사용법

소정·2023년 2월 27일
0

Android_with_Java

목록 보기
17/33

OpenAPI (Open AplicationInterface)

API는 대량의 문자열 정보를 가지고 있다
이 대량의 대이터를 구분하기 위한 방법이 필요함
Open API 제공 기관에서 데이터를 줄때 각 값들을 구분하기 위해 3가지 데이터 표기형식(Representation of Resource)을 사용하였음

[1] CSV방식 (comma separate value)

sam,robin,hong...
,를 하용해 데이터를 구분함
자바에서 split()을 사용해 손쉽게 배열객체로 만듦

💡 [단점]
데이터 자체에 무엇을 나태내는지 표기가 없어서 헷갈림 그래서 나온 것이 xml 방식

[2] xml (eXtensiable markup language)

마크업을 통해 글씨를 씀
태그문<></>을 이용해서 정보를 구분해 담음

리사이클뷰에 보여주면됨

<item>
        <title>왕십리약국</title>
        <addr>서울시 도선동</addr>
        <tel>02-214-2356</tel>
</item>
<item>
        <title>이수약국</title>
        <addr>서울시 사당동</addr>
        <tel>02-456-7892</tel>
</item>

사용방법

xml 분석하는 class 존재 (Xml~Parser)
① res 폴더에서 가져온다 => XmlResourceParser
② 서베용 분석가 => XmlPullParser

1. XmlResourceParser (Resouce)

  • 내 프로젝트 안에 xml 넣어서 파싱 연습해보기
  • 파싱 연습을 위한 예제

💡 xml가 읽는 방법

특정 단위별로 읽음, next할때마다 읽는 걸 이벤트라고 부름
단위 5가지(이벤트 타입)
1. 스타트 도큐먼트
2. 스타크 태그
3. TEXT
4. 엔드 태그
5. 엔트 도큐먼트


main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/btn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="parse xml from resource"/>

    <TextView
        android:id="@+id/tv"
        android:textColor="@color/black"
        android:padding="8dp"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</LinearLayout>

파싱할 xml 내 폴더에 넣어두기 xml for resouce

<?xml version="1.0" encoding="utf-8"?>
<movies>

    <item>
        <no>1</no>
        <title>Alien</title>
        <ganre>SF, ACTION, ADVENTURE</ganre>
    </item>

    <item>
        <no>2</no>
        <title>AVATA</title>
        <ganre>SF, ACTION, ADVENTURE</ganre>
    </item>

    <item>
        <no>3</no>
        <title>Notting Hill</title>
        <ganre>ROMENCE, MELO</ganre>
    </item>

    <item>
        <no>4</no>
        <title>Nightmare</title>
        <ganre>HORROR, THRILLER</ganre>
    </item>


</movies>

main.java

① res폴더 창고관리자 소환
Resources res = getResources();
② 창고 관리자로부터 파서 객체 얻어오기
XmlResourceParser xrp = res.getXml(R.xml.movies);
③ xml 파서에게 분석 작업 요청

  • textView에 StringBuffer로 저장해놨다가 한번에 보여주기
package com.bsj0420.ex52xmlresouceparsing;

import androidx.appcompat.app.AppCompatActivity;

import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.os.Bundle;
import android.widget.TextView;

import org.xmlpull.v1.XmlPullParserException;

import java.io.IOException;

public class MainActivity extends AppCompatActivity {

    TextView tv;

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

        tv = findViewById(R.id.tv);

        findViewById(R.id.btn).setOnClickListener(view -> clickBtn());

    }

    void clickBtn(){
        //Res 폴더에 있는 xml 문서를 읽어와서 분석(parse)하는 작업 수행

        //1. res폴더 창고관리자 소환
        Resources res = getResources();

        //2. 창고 관리자로부터 파서 객체 얻어오기
        XmlResourceParser xrp = res.getXml(R.xml.movies);
        
        //3. xml 파서에게 분석 작업 요청
        try {
            xrp.next(); //스트림 작업은 위험 - 예외처리 해야됨
            int eventType = xrp.getEventType(); //리턴값 int 단위 5가지를 숫자로 리턴받음
            
            //스트링을 가지고 있는 작은 저장 공간
            StringBuffer buffer = new StringBuffer();
//            buffer.append("aa"); //append()로 쌓아둠
//
//            String s = buffer.toString(); //이때 메모리에 객체 만듦!!!

//            while (true) {
//
//                xrp.next();
//                eventType = xrp.getEventType(); //무한이 돌면서 eventType 받아와라
//
//                if(eventType == XmlResourceParser.END_DOCUMENT) break;
//            }

            // 축약 1 : next()하면 그 순간 이벤트 값이 오기때문에  getEventType() 따로 쓸 필요없음
//            while (true) {
//                eventType = xrp.next();
//                if(eventType == XmlResourceParser.END_DOCUMENT) break;
//            }

            //축약 2 : if 조건을 () 안으로
            while (eventType != XmlResourceParser.END_DOCUMENT) {

                switch (eventType) {
                    case XmlResourceParser.START_DOCUMENT:
                        buffer.append("----파싱 시작---- \n\n");
                        break;
                    case XmlResourceParser.END_DOCUMENT:
                        break;
                    case XmlResourceParser.START_TAG:
                        String tagName = xrp.getName(); //태그 이름이 많으니까 구분하기 위해

                        if(tagName.equals("item")) {
                            //아무것도 안썼으니 그냥 넘어감
                        } else if (tagName.equals("no")) {
                            buffer.append("순위 : ");
                        } else if (tagName.equals("title")) {
                            buffer.append("제목 : ");
                        } else if (tagName.equals("genre")) {
                            buffer.append("장르 : ");
                        }
                        break;
                    case XmlResourceParser.TEXT:

                        String text = xrp.getText(); //사이 텍스트값
                        buffer.append(text);

                        break;
                    case XmlResourceParser.END_TAG:
                        String tagName2 = xrp.getName();

                        if(tagName2.equals("item")){ //끝나는 태그랑 만났니?
                            buffer.append("\n---------------------\n");
                        } else if (tagName2.equals("no")) {
                            buffer.append("\n");
                        } else if (tagName2.equals("title")) {
                            buffer.append("\n");
                        } else if (tagName2.equals("genre")) {
                            buffer.append("\n");
                        }

                        break;
                }

                eventType = xrp.next();
            }//

            // 파싱 끝
            buffer.append("\n\n -- 파싱을 완료했습니다-- \n\n");

            tv.setText(buffer.toString()); //이때 메모리에 만듦

        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (XmlPullParserException e) {
            throw new RuntimeException(e);
        }

        //문자열을 계속 결합해야할땐 

    }

}

🧨 StringBuffer란?

StringBuffer
StringBuffer buffer = new StringBuffer();
buffer.append("aa"); //append()로 쌓아둠
String s = buffer.toString(); //이때 메모리에 객체 만듦!!!


String s = "";
s += "aa";
s += "bb";
이렇게 String을 더하면 매번 메모리에 새로운 객체가 생성돼서 메모리를 쓸데없이 많이 쓰게됨 때문에 문자를 쌓아두다가 한번에 객체를 만드는 StringBuffer 를 사용한다



2. XmlPullParser (네트워크)

  • 네트워크에서 받아오기

작업 순서

1.recycle가 잘 동작하는지 부터 확인 (recycle부터 만들기)
① main.xml 에 recycleView 만들기
② item class 만들기

item.java

package com.bsj0420.ex53xmlpullparsermovie;

public class MovieItem {

    String rank;
    String movieNm;
    String openDt;
    String audiAcc;

    public MovieItem(){
    }

    public MovieItem(String rank, String movieNm, String openDt, String audiAcc) {
        this.rank = rank;
        this.movieNm = movieNm;
        this.openDt = openDt;
        this.audiAcc = audiAcc;
    }
}

③ mian.java에서 가상의 데이터 넣고 recycleView 확인

  • movieItems.add(new MovieItem()); 해보기

main.java

package com.bsj0420.ex53xmlpullparsermovie;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

    //영화정보를 가진 무비 아이템을 여러개 관리하는 리스트객체 생성
    ArrayList<MovieItem> movieItems = new ArrayList<>();

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

        //원래는 서버의 xml을 읽어와야하지만
        // recyclerView의 동작 테스트를 위해 가상의 값을 추가해보기
        movieItems.add(new MovieItem("1","영화이름","2022-02-27","15422"));

    }
}

④ adapter 만들기

adapter.java

package com.bsj0420.ex53xmlpullparsermovie;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import java.util.ArrayList;

public class MovieAdapter extends RecyclerView.Adapter<MovieAdapter.VH> {

    Context context;
    ArrayList<MovieItem> items;

    public MovieAdapter(Context context, ArrayList<MovieItem> items) {
        this.context = context;
        this.items = items;
    }

    @NonNull
    @Override
    public VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(context).inflate(R.layout.recycler_item,parent,false);
        return new VH(view);
    }

    @Override
    public void onBindViewHolder(@NonNull VH holder, int position) {
        //현재 번째 데이터면 MovieItem얻어오기
        MovieItem item = items.get(position);

        holder.tvRank.setText(item.rank);
        holder.tvTitle.setText(item.movieNm);
        holder.tvOpenDt.setText(item.openDt);
        holder.tvAudiAcc.setText(item.audiAcc);
    }

    @Override
    public int getItemCount() {
        return items.size();
    }

    class VH extends RecyclerView.ViewHolder {

        TextView tvRank, tvTitle, tvOpenDt, tvAudiAcc;

        public VH(@NonNull View itemView) {
            super(itemView);

            tvRank = itemView.findViewById(R.id.tv_rank);
            tvTitle = itemView.findViewById(R.id.tv_title);
            tvOpenDt = itemView.findViewById(R.id.tv_open_date);
            tvAudiAcc = itemView.findViewById(R.id.tv_audiAcc);
        }
    }

}

⑤ 연결 확인

main.java

package com.bsj0420.ex53xmlpullparsermovie;

import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.RecyclerView;

import android.os.Bundle;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

    //영화정보를 가진 무비 아이템을 여러개 관리하는 리스트객체 생성
    ArrayList<MovieItem> movieItems = new ArrayList<>();

    RecyclerView recyclerView;
    MovieAdapter adapter;

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

        //원래는 서버의 xml을 읽어와야하지만
        // recyclerView의 동작 테스트를 위해 가상의 값을 추가해보기
        movieItems.add(new MovieItem("1","영화이름","2022-02-27","15422"));

        //아답터 연결
        recyclerView = findViewById(R.id.recycler);
        adapter = new MovieAdapter(this,movieItems);
        recyclerView.setAdapter(adapter);

    }
}
  1. xml으로 오는 openAPI 네트워크 작업 (->clickBtn() 함수 보기)
    ① 매니패스트에 인터넷 사용에 대한 퍼미션 & https가 아닌 http도 받겠다 명시

네트워크 작업은 오래 걸리는 작업으로 인식됨 -> 반드시 별도의 스레드가 작업

new Thread().start(); 
//=> start() 끝나기 전에 {} 중괄호 열어서 해야할 일 쓴다

new Thread(){
   @Override
   public void run() {
       //네트워크 작업 비동기로 시작.
   }
}.start();

③ 위 서버 url 위치까지 무지개로드(stream)를 열어주는 해임달 객체() 생성

URL url = new URL(address); //스트림은 예외처리 필수

④ url 통해 무지개 로드(스트림) 열기

InputStream is  =url.openStream(); //문자를 바이트 단위로 읽어옴
InputStreamReader isr = new InputStreamReader(is); //바이트 => 문자로 변환

⑤ xml 문서를 조금 더 쉽게 분석(parse) 해주는 객체 생성
=> XmlPullParser 만들기 위한 공장 필요 (factory 빌드패턴)
XmlPullParser는 공장을 통해서 만들어야함

XmlPullParserFactory factory = XmlPullParserFactory.newInstance();

XmlPullParser xpp = factory.newPullParser();

⑥ XmlPullParser에게 스트림 주기

xpp.setInput(isr);

⑦ xml Parser에게 분석 작업

  • XmlPullParser는 시작부터 스타트도큐먼트임
    (XmlResourceParser 처럼 먼저 .next(); 할 필요 없다~!)
  • 바로 getEventType() 한 후 int eventType으로 리턴 받기
  • Swich문 밖에서 MovieItem movieItem = null; 놓고
  • case중 XmlPullParser.START_TAG 에서 성자가 빈 MovieItem 객체 생성
  • 태그문 만날때 마다 바로바로 movieItem에 text 넣어주기
  • XmlPullParser.END_TAG 어레이리스트에 add

main.java 총 코드

click() 메소드 안 잘 보기~!

package com.bsj0420.ex53xmlpullparsermovie;

import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.RecyclerView;

import android.os.Bundle;
import android.widget.Toast;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;

public class MainActivity extends AppCompatActivity {

    //영화정보를 가진 무비 아이템을 여러개 관리하는 리스트객체 생성
    ArrayList<MovieItem> movieItems = new ArrayList<>();

    RecyclerView recyclerView;
    MovieAdapter adapter;

    String apiKey = "f5eef3421c602c6cb7ea224104795888";

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

        //원래는 서버의 xml을 읽어와야하지만
        // recyclerView의 동작 테스트를 위해 가상의 값을 추가해보기
        //movieItems.add(new MovieItem("1","영화이름","2022-02-27","15422"));

        //아답터 연결
        recyclerView = findViewById(R.id.recycler);
        adapter = new MovieAdapter(this,movieItems);
        recyclerView.setAdapter(adapter);

        findViewById(R.id.btn).setOnClickListener(view -> clickBtn());

    }

    void clickBtn() {
        //영화진흥원의 open API 정보(일일 박스오피스)를 가져와서 리사이클러뷰에 보여주기
        //xml파일 포멧으로 되어있음
        
        //1. 네트워크 작업은 권한이 반드시 요구됨.[ AndroidManifest.xml 에서 권한부여 ]
        
        //2. 네트워크 작업은 오래 걸리는 작업으로 인식됨
        //반드시 별도의 스레드가 작업해야한다

        new Thread(){
            @Override
            public void run() {
                //네트워크 작업 비동기로 시작.

                //xml문서의 REST url주소
                
                //검색 날짜 [오늘 날짜의 전날]
                Date date = new Date(); //이 객체가 생성되는 순간의 날짜와 시간을 가짐
                //date.setTime(); // 19700101 0시 0분 0초가 기준

                date.setTime(date.getTime() - (1000*60*60*24)); //하루전 시간 뺀 것

                //특정 포멧으로 날짜를 문자열로 만들어주는 객체
                SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd"); //mm 쓰면 분 나옴

                String yesterday = sdf.format(date);
                
                // 샘플 주소
                String address = "http://kobis.or.kr/kobisopenapi/webservice/rest/boxoffice/searchDailyBoxOfficeList.xml"
                        +"?key="+apiKey
                        +"&targetDt="+yesterday
                        +"&itemPerPage=5";

                // 위 서버 url 위치까지 무지개로드를 열어주는 해임달 객체 생성
                try {
                    URL url = new URL(address); //스트림은 예외처리 필수

                    //무지개 로드(스트림) 열기
                    InputStream is  =url.openStream(); //문자를 바이트 단위로 읽어옴
                    InputStreamReader isr = new InputStreamReader(is); //바이트 => 문자로 변환

                    //xml 문서를 조금 더 쉽게 분석(parse) 해주는 객체 생성
                    XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
                    // => XmlPullParser 만들기 위한 공장 필요 factory 빌드패턴

                    XmlPullParser xpp = factory.newPullParser(); //공장을 통해서 만들어야함

                    // XmlPullParser에게 스트림 주기 
                    xpp.setInput(isr);

                    //xml Parser에게 분석 작업
                    //XmlPullParser는 시작부터  스타트도큐먼트임
                    //따라서 .next(); 먼저 할 필요없다
                    int eventType = xpp.getEventType();

                    //영화 한개 정보 참조변수
                    MovieItem movieItem = null;

                    while (eventType != XmlPullParser.END_DOCUMENT) {

                        switch (eventType) {
                            case XmlPullParser.START_DOCUMENT:

                                //별도의 스레드는 ui작업 불가능 그래서 ui스레드에서 작업하도록 요청
                                runOnUiThread(new Runnable() {
                                    @Override
                                    public void run() {
                                        Toast.makeText(MainActivity.this, "파싱 시작", Toast.LENGTH_SHORT).show();
                                    }
                                });
                                
                                break;
                            case XmlPullParser.START_TAG:

                                String tagName = xpp.getName(); //태그이름 얻어오기

                                if(tagName.equals("dailyBoxOffice")){
                                    movieItem = new MovieItem(); //생성자가 빈걸로 객체 생성 (빈통 만들어 놓기)
                                } else if (tagName.equals("rank")) {

                                    xpp.next(); //rank 스타트 태크 만나면 바로 text 로 가라
                                    movieItem.rank = xpp.getText(); //거기 써있는 글씨 가져와서 movieItem에 넣어

                                } else if (tagName.equals("movieNm")) {

                                    xpp.next();
                                    movieItem.movieNm = xpp.getText(); //XmlPullParser.TEXT 안해도 됨

                                } else if (tagName.equals("openDt")) {

                                    xpp.next();
                                    movieItem.openDt = xpp.getText();

                                } else if (tagName.equals("audiAcc")) {

                                    xpp.next();
                                    movieItem.audiAcc = xpp.getText();

                                }

                                break;

                            case XmlPullParser.END_TAG:
                                String tagName2 = xpp.getName();
                                if(tagName2.equals("dailyBoxOffice")){
                                    //영화 하나 끝났으면
                                    //리사이클러의 어레이리스트에 add
                                    movieItems.add(movieItem);
                                }
                                break;
                            case XmlPullParser.TEXT:
                                break;
                        }

                        eventType = xpp.next();
                    }//while....

                    //UI작업은 ui thread 위에서 작업
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            Toast.makeText(MainActivity.this, "파싱 완료", Toast.LENGTH_SHORT).show();
                            
                            //대량의 데이터가 새로이 추가 됐다고 아답터한테 공지해야만
                            //리사이클러뷰가 화면을 개신한다
                            adapter.notifyDataSetChanged();
                        }
                    });
                    

                } catch (MalformedURLException e) {
                    throw new RuntimeException(e);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                } catch (XmlPullParserException e) {
                    throw new RuntimeException(e);
                }


            }
        }.start();
    }


}

📢 날짜 얻어오기와 포멧

1. Date()

Data 객체가 new 키워드로 생성되는 순간의 날짜와 시간을 가짐
Date date = new Date(); => 현재 시간을 가진 date

2. setTime()

date.setTime(); // 19700101 0시 0분 0초가 기준

date.setTime(date.getTime() - (10006060*24)); //하루전 시간 뺀 것

3. SimpleDateFormat()

특정 포멧으로 날짜를 문자열로 만들어주는 객체
1.포멧을 정한 객체 생성
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd"); //mm 쓰면 분 나옴
2.포멧 대입
String yesterday = sdf.format(date);



💡 [단점]
지금은 xml이 태그문때문에 너무 문자양이 길어져서 지금은 바꾸는 중.[개발자의 코드도 좀 지저분함]
지금은 json 이라는 표기문법을 사용함. - 이게 가장 많이 사용됨



[3] Json 방식(Javascript Object Notation)

파일 포멧 형식
{"name:sam","age:20"...} key-value 쌍으로 저장

[  {"title":"왕십리약국","addr":"서울","tel":1234}  , {"title":"이수약국","addr":"인천","tel":8887}    ]

그래서 기관마다 xml 또는 json 으로 정보를 제공함.
각각의 데이터만 주는 경우도 있고
둘 모두를 제공해 주는 경우도 있음.

공공데이터 읽는방법


① 특정한 컴퓨터의 정보를 가져오는 약속 (GET/POST 방식)
② 현재 이 데이터가 저장되어 있는 방식
③ URL + path 데이터가 저장되어 있는 위치
http://0/0/0?dataTitle=조광&pageSize=0 3번 뒤에 붙는 데이터 정보
⑤ 태그문으로 구분되어 있는 데이터들

profile
보조기억장치

0개의 댓글