[안드로이드 스튜디오] OpenAPI 데이터 받아오기 - xml parsing

2021. 1. 11. 16:36개발 관련/안드로이드 스튜디오

XML 파싱 방법

  1. DOM (Document Object Model ) Parser
    • Dom parser ( Document, DocumentBuilder )
    • jsoup : 자바 HTML 파서
  2. SAX (Simple API for XML ) Parser
    • XML Pull Parser

 

1. DOM 파서

  • xml 문서를 파싱 -> 1:1로 매핑되는 object로 생성 -> 메모리에 문서 구조 그대로인 Tree 구조 ( 리턴된 Object ) 로 적재

  • 끝까지 파싱하고 객체를 생성하여 리턴하기 전까지는 다른 event 발생 불가 ( 완료될 때까지 대기 )

  • + 한번 파싱하면 언제든 원하는 element 정보를 얻을 수 있다( name, text, attribute )

  • + root, child, parent, sibling 노드 탐색 기능을 사용할 수 있다

  • + DOM 객체를 수정하여 xml로 문서화할 수 있다. ( Generator 기능 )

  • - 문서가 커질수록 메모리를 많이 소모한다.

  • 추천 : xml 문서 탐색이 빈번하게 발생할 경우, 문서 구조가 자주 변경되는 경우

- DocumentBuilder 이용

 

- Jsoup 파서

 

  • Java에서 HTML을 파싱하기 위해 만들어진 library

  • build.gradle(Module.app)에 의존성 추가 -> sync now

 

 

 

 

 

2. SAX 파서

  • event 기반 파서

  • xml 문서를 단방향 스트림( 라인 단위 )으로 취급하여 읽어들이는 중에 유효한 element가 식별되면 event를 parser의 외부로 전달, 처리 ( 사용자가 처리 )

  • + 동작 방식 간단

  • + 필요한 요소만 식별 시 속도 빠름

  • + 적은 메모리 소요 ( 개발자가 의도적으로 남긴 것만 메모리에 남는다 )

  • - 그냥 지나갔던 정보를 얻고 싶을 때 다시 파싱해야하는 번거로움 ( 자유로운 탐색 x )

  • - 추가, 수정, 삭제, 이동 구현 어려움 ( event로부터 데이터를 Tree로 구조화해야하는 작업 필요 -> DOM이 훨씬 효율적 )

  • 추천 : 요소의 값이 중요한 경우 ( 문서의 구조 x ), xml 문서의 구조가 간단하거나 동일한 요소가 반복되는 경우

 

- XML Pull 파서

  • XmlPullParserFactory를 통해 XmlPullParser 인스턴스 생성하거나 newPullParser() 함수를 통해 생성
  • event 기반 파서 (= SAX 파서 )

  • 안드로이드에서 기본적으로 지원되는 xml 파서 ( XmlPullParser 인터페이스 )
  • XmlPullParserFactory.newPullParser() 함수를 통해 KXmlParser,
    Xml.newPullParser() 함수를 통해 ExpatPullParser 생성 가능
    ( 둘 다 XmlPullParser을 통해 xml 파싱 관련 함수 호출 가능 )
  • 모든 파싱을 하지 않고도 특정 부분까지의 파싱내용을 활용

  • + 원하는 부분 파싱

  • - 그냥 지나갔던 정보를 얻고 싶을 때 다시 파싱해야하는 번거로움

  • - SAX 파서보다 느리다.

 

 


공공데이터 가져오기

 

1. ../main/res/xml/network_config.xml 생성

/res/xml/network_config.xml 생성

 

 

2. network_config.xml 내용

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config cleartextTrafficPermitted="true">
        <domain includeSubdomains="true">openapi.gg.go.kr/AnimalPharmacy?</domain>
    </domain-config>
</network-security-config>

 

 

3. Permission 추가 - AndroidManifest.xml

<uses-permission android:name="android.permission.INTERNET" />

 

 

4. 이후는 깃 참조 : 

 

 

 


ERROR :

  •  Cleartext HTTP traffic to ~ not permitted

 

network_config.xml을 manifest에 등록을 안해서 생긴 오류

% in AndroidManifest.xml

<application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme"
        android:networkSecurityConfig="@xml/network_config" > % <- 여기 추가

 

 

  • DOM parser 중 url이 null으로 읽힘 -> 해결 x
System.err: org.xml.sax.SAXParseException: Unexpected token (position:TEXT https://openapi....@1:77 in java.io.StringReader@b88ccaf) 
...

2021-01-20 00:37:15.788 15951-16062/com.androidapp.practiceapp E/AndroidRuntime: FATAL EXCEPTION: AsyncTask #1
    Process: com.androidapp.practiceapp, PID: 15951
    java.lang.RuntimeException: An error occurred while executing doInBackground()
        at android.os.AsyncTask$3.done(AsyncTask.java:354)
        at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:383)
        at java.util.concurrent.FutureTask.setException(FutureTask.java:252)
        at java.util.concurrent.FutureTask.run(FutureTask.java:271)
        at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:245)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at java.lang.Thread.run(Thread.java:764)
     Caused by: java.lang.NullPointerException: Attempt to invoke interface method 'org.w3c.dom.Element org.w3c.dom.Document.getDocumentElement()' on a null object reference
        at com.androidapp.practiceapp.OpenAPIDistinct.doInBackground(OpenAPIDistinct.java:50)
        at com.androidapp.practiceapp.OpenAPIDistinct.doInBackground(OpenAPIDistinct.java:21)
        at android.os.AsyncTask$2.call(AsyncTask.java:333)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:245) 
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167) 
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641) 
        at java.lang.Thread.run(Thread.java:764) 

Jsoup에서는 이상이 없었으나 DocumentBuilder으로 적용시켰을 때 url을 null로 읽는 문제가 발생.

MainActivity에서 실행했을 때도 마찬가지였다.

 

 

  • XML Pull Parser 구현 중 OpenAPIDistinct는 적절한 결과가 나오나 OpenAPI는 에러 발생
2021-01-20 02:19:53.806 25086-25239/com.androidapp.practiceapp W/System.err: org.xmlpull.v1.XmlPullParserException: Expected a quoted string (position:DOCDECL @1:50 in java.io.InputStreamReader@90995fb) 
2021-01-20 02:19:53.807 25086-25239/com.androidapp.practiceapp W/System.err:     at org.kxml2.io.KXmlParser.readQuotedId(KXmlParser.java:671)
2021-01-20 02:19:53.807 25086-25239/com.androidapp.practiceapp W/System.err:     at org.kxml2.io.KXmlParser.readExternalId(KXmlParser.java:650)
2021-01-20 02:19:53.807 25086-25239/com.androidapp.practiceapp W/System.err:     at org.kxml2.io.KXmlParser.readDoctype(KXmlParser.java:591)
2021-01-20 02:19:53.807 25086-25239/com.androidapp.practiceapp W/System.err:     at org.kxml2.io.KXmlParser.next(KXmlParser.java:421)
2021-01-20 02:19:53.807 25086-25239/com.androidapp.practiceapp W/System.err:     at org.kxml2.io.KXmlParser.next(KXmlParser.java:313)
2021-01-20 02:19:53.807 25086-25239/com.androidapp.practiceapp W/System.err:     at com.androidapp.practiceapp.OpenAPI.doInBackground(OpenAPI.java:92)
2021-01-20 02:19:53.807 25086-25239/com.androidapp.practiceapp W/System.err:     at com.androidapp.practiceapp.OpenAPI.doInBackground(OpenAPI.java:14)
2021-01-20 02:19:53.807 25086-25239/com.androidapp.practiceapp W/System.err:     at android.os.AsyncTask$2.call(AsyncTask.java:333)
2021-01-20 02:19:53.807 25086-25239/com.androidapp.practiceapp W/System.err:     at java.util.concurrent.FutureTask.run(FutureTask.java:266)
2021-01-20 02:19:53.807 25086-25239/com.androidapp.practiceapp W/System.err:     at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:245)
2021-01-20 02:19:53.807 25086-25239/com.androidapp.practiceapp W/System.err:     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
2021-01-20 02:19:53.808 25086-25239/com.androidapp.practiceapp W/System.err:     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
2021-01-20 02:19:53.808 25086-25239/com.androidapp.practiceapp W/System.err:     at java.lang.Thread.run(Thread.java:764)

위의 경우와 같은 에러 발생

async가 두 곳에서 쓰여서 그런걸까?


참고 : 

openAPI 데이터 가져오기 : android55.tistory.com/5

Data Load 방법 : recipes4dev.tistory.com/134

 

동물 약국(경기도) 공공데이터 페이지 : data.gg.go.kr/portal/data/service/selectServicePage.do?page=1&rows=10&sortColumn=&sortDirection=&infId=1664G9P3897924K608Z3588543&infSeq=3&order=&loc=&searchWord=%EB%8F%99%EB%AC%BC%EC%95%BD%EA%B5%AD+%ED%98%84%ED%99%A9 

 

Jsoup 관련 : offbyone.tistory.com/116

DOM parser (DocumentBuilder 관련 ) : ddangeun.tistory.com/65

 blog.naver.com/PostView.nhn?blogId=javaking75&logNo=140179651659

m.blog.naver.com/PostView.nhn?blogId=hisukdory&logNo=50085040663&proxyReferer=&proxyReferer=https:%2F%2Fwww.google.com%2F

m.blog.naver.com/qbxlvnf11/221324667993

 

 

XML Pull Parser : recipes4dev.tistory.com/134  

all-dev-kang.tistory.com/entry/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-%EA%B3%B5%EA%B3%B5%EB%8D%B0%EC%9D%B4%ED%84%B0-api-%ED%8C%8C%EC%8B%B1-%EC%98%88%EC%8B%9C-%EB%B0%8F-%EA%B7%B8%EB%9E%98%ED%94%84-%ED%91%9C%ED%98%84

 

 


error 관련 : 

 

http 접속 오류 -> androidManifest.xml 추가 : developside.tistory.com/85