JSON
웹 응용 프로그램 개발에는 XML이 대세이다. 표준화된 포맷이므로 이기종간의 통신에도 아무런 문제가 없고 표현력도 나름대로 풍부하기 때문이다. 그러나 XML은 SGML을 간략화한 포맷임에도 불구하고 규칙이 무척 복잡하고 까다로와 파싱하는데 고려해야 할 것들이 무척 많다. 그래서 XML 문서는 통상 별도의 전문 파서를 사용한다. 그러나 파서도 XML 스팩의 모든 규칙을 준수해 가며 예외적인 상황까지 다 처리해야 하므로 느릴 수밖에 없다.
CPU 빵빵하고 메모리가 넉넉한 PC 환경에서는 왠만한 크기의 XML 문서라도 별 문제가 없다. 그러나 자원이 제한된 모바일 환경에서 XML의 비효율성은 치명적이다. 50K 문서 하나를 다운받은 후 DOM으로 원하는 정보를 객체로 뽑아 내는데 최소 15초가 걸리며 길면 30초까지 기다려야 한다. 자바의 문자열 처리가 C보다 굼뜨는데다 모바일 네트워크까지 느려 진짜 까무라칠 지경이었다. 그래서 XML의 대안을 찾아 봤는데 JSON이라는 좋은 방법이 있어 여기에 사용 방법을 간략하게 정리해 놓기로 한다.
JSON(Java Script Object Notation)은 웹에서 정보를 주고 받는 경량화된 방법이다. 이름이 의미하듯이 자바 스크립트에서 주로 사용되는 방법이지만 다른 프로그래밍 언어에서도 충분히 사용할 수 있다. 자바의 수치, 논리, 문자열, 객체, 배열 타입이 다른 언어에도 모두 형태만 다를 뿐 존재하기 때문이다. JSON은 무엇보다 프로그래밍 언어의 변수들을 전달하는데 효율적이다. 배열, 객체 등을 표현할 수 있어 복잡하고 거대한 객체 배열을 표현할 수 있다. 다음은 JSON의 특징 및 장점이다.
- 데이터 파일은 단순한 유니코드 텍스트 파일이므로 사람이 읽을 수도 있고 직접 편집도 가능하다.
- 단순한 텍스트 파일이므로 텍스트를 읽고 쓸 수 있는 모든 언어나 플랫폼에서 사용 가능하다.
- RFC 4627로 포맷이 규격화되어 있어 나름대로 표준이 정립되어 있다. 그만큼 많이 쓴다는 얘기다.
- 대부분의 언어에 JSON 파서가 라이브러리 형태로 제공된다.
JSON에 저장되는 정보의 형태는 다음 세 가지이다.
1.배열 : 대괄호안에 값을 콤마로 구분하여 나열한다. 대괄호안에 나오는 순서대로 배열 요소의 순서가 매겨진다.
[값1, 값2, ....]
예를 들어 크기 3의 정수형 배열이라면 [1, 2, 3 ] 식으로 표기한다.
2.객체 : 중괄호안에 이름:값의 형태로 멤버 하나를 표현하고 각 멤버는 콤마로 구분한다. 멤버의 순서는 의미가 없다. 이름은 가급적이면 따옴표로 둘러싸는 것이 좋다.
{이름:값, 이름:값, ...}
예를 들어 사람 하나의 신상 정보라면 {"이름":"김상형", "나이":29 } 식으로 표기한다.
3.단순 값 : 수치, 문자열, 논리형, null 4가지 타입을 지원한다.
- 수치 : 10진수만 지원하며 소수점을 포함한 고정 소수점, 지수 표기식인 부동 소수점을 지원한다. 언어에 따라서 8진수나 16진수를 지원하는 경우도 있다.
- 문자열 : 따옴표로 감싸 표기하며 특수 기호는 확장열로 이스케이프한다. 확장열은 C나 자바와 거의 유사하다. 예를 들어 따옴표 자체는 \"로 표기하고 \n은 개행이다.
- 논리형 : true 또는 false중 하나이다.
- null : 빈 객체를 표현한다.
표현할 수 있는 타입이 굉장히 작은 것 같지만 중첩을 허용하기 때문에 이 정도만으로도 대부분의 언어에서 사용하는 타입을 무리없이 표기할 수 있다. 배열안의 값에 객체가 들어갈 수 있고 객체안에는 또 배열이 들어갈 수 있다. 이런 중첩은 프로그래밍을 조금이라도 해 본 사람이라면 아주 익숙할 것이다. 다음은 주문 정보를 표현한 JSON 파일이다.
[{"product":"Mouse", "Maker":"Samsung", "Price":23000},{"product":"KeyBoard", "Maker":"LG", "Price":12000},{"product":"HDD", "Maker":"Western Digital", "Price":156000}]
주문 정보 하나는 제품명, 제조사, 가격 등을 멤버로 가지는 객체이고 그런 객체 세 개가 배열을 구성하고 있다. 이 배열이 다른 객체의 멤버가 될 수 있으며 객체는 또 다른 객체의 멤버나 배열의 요소가 될 수 있다. 그러므로 표현할 수 있는 정보의 양은 거의 무한하다.
다음은 JSON 파일을 읽어들이는 방법에 대해 알아 보자. 언어별로 JSON 파서를 제공하는데 여기서는 자바의 org.json 패키지가 제공하는 클래스만 소개한다. 자바의 기본 패키지에는 포함되어 있지 않은데 안드로이드에는 기본 포함이므로 별다른 설정없이 사용할 수 있다. 다른 언어들이나 플랫폼에서도 거의 비슷할 것이다.
JSONArray 클래스는 배열을 읽어들인다. 생성자로 JSON 문자열을 전달하면 파싱 후 배열로 가지고 있을 것이다. 배열에 저장된 값을 읽을 때는 다음 메서드를 호출한다. 물론 배열 요소의 타입은 미리 알고 있어야 한다.
Object get (int index)
int getInt (int index)
String getString (int index)
boolean getBoolean (int index)
JSONArray getJSONArray (int index)
JSONObject getJSONObject (int index)
get 메서드는 Object 타입을 리턴하므로 원하는 타입으로 캐스팅해서 대입받아야 한다. 나머지 메서드는 타입을 분명히 지정하므로 캐스팅을 할 필요가 없다. 인수로는 배열의 첨자를 전달한다. 만약 첨자가 발견되지 않으면 예외를 발생시킨다. get 외에 opt* 메서드도 제공되는데 이 메서드는 첨자가 발견되지 않을 때 예외를 발생시키는 대신 null, 0, flase 등의 디폴트값을 리턴한다는 점이 다르다.
JSONObject 클래스는 JSON 파일에서 객체를 읽어들인다. 생성자로 문자열을 전달하면 파싱 후 객체의 멤버 집합을 가지고 있을 것이다. 멤버들을 읽을 때는 다음 메서드를 호출한다. 읽고자하는 멤버의 이름은 미리 알고 있어야 한다.
Object get (String key)
int getInt (String key)
String getString (String key)
JSONArray의 메서드와 목록은 거의 유사하되 첨자가 아니라 멤버의 이름을 인수로 전달받는다는 점이 다르다. 배열은 첨자로 요소를 참조하지만 객체는 이름으로 멤버를 참조하기 때문이다. 멤버가 존재하지 않으면 예외가 발생하며 예외 대신 디폴트를 리턴하는 opt* 메서드가 제공된다는 점도 배열과 동일하다.
다음 예제는 정수형 배열을 정의하는 JSON 파일에서 각 정수들을 추출하여 총 합계를 구한다. 안드로이드 환경에서 작성했으며 대화상자로 결과를 알려 준다.
String Json = "[8,9,6,2,9]";
try {
int sum = 0;
JSONArray ja = new JSONArray(Json);
for (int i = 0; i < ja.length(); i++) {
sum += ja.getInt(i);
}
new AlertDialog.Builder(this)
.setMessage("Sum = " + sum)
.setTitle("Result").show();
} catch (JSONException e) {;}
Json 테스트 문자열에는 정수 배열이 저장되어 있다. 이 문자열을 JSONArray의 생성자로 전달하여 파싱하고 요소 개수만큼 루프를 돌며 getInt 메서드로 값을 꺼내면 된다. 34라는 총 합계가 리턴될 것이다. 다음 예제는 좀 더 복잡한 객체의 배열을 읽어들인다.
String Json = "[{\"Product\":\"Mouse\", \"Maker\":\"Samsung\", \"Price\":23000},"
+ "{\"Product\":\"KeyBoard\", \"Maker\":\"LG\", \"Price\":12000},"
+ "{\"Product\":\"HDD\", \"Maker\":\"Western Digital\", \"Price\":156000}]";
try {
String Result = "주문 목록\n";
JSONArray ja = new JSONArray(Json);
for (int i = 0; i < ja.length(); i++) {
JSONObject order = ja.getJSONObject(i);
Result += "제품명:" + order.getString("Product") +
",제조사:" + order.getString("Maker") +
",가격" + order.getInt("Price") + "\n";
}
new AlertDialog.Builder(this)
.setMessage(Result)
.setTitle("Result").show();
} catch (JSONException e) {
Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show();
}
배열을 읽는 부분은 앞 예제와 동일하다. 다만 배열의 요소가 객체이므로 getJSONObject 메서드로 요소를 읽어야 하며 각 객체의 멤버도 일일이 읽어야 한다는 점이 다르다. 주문 내역을 문자열 하나로 깔끔하게 정리한 후 출력할 것이다.
JSON은 XML에 비해서 무척 단순한 포맷이다. JSON이 XML을 완전히 대체하지는 못하겠지만 리소스가 부족한 환경에서는 XML의 대체품으로 충분히 실용적이다. 사실 프로그램끼리 네트워크를 통해 자료를 전달하는데 있어 이보다 더 복잡할 필요가 없다. 문자열, 수치만 해도 왠만한 정보는 다 표현할 수 있고 배열, 객체까지 지원하면 사실상 모든 정보를 다 전달할 수 있는 셈이다. 단순한만큼 속도도 만족스럽고 직관적이기도 하다.
http://winapi.egloos.com/3322992 -> 퍼온곳