관리 메뉴

나만을 위한 블로그

[Android] Gson이란? Gson 사용법 본문

Android

[Android] Gson이란? Gson 사용법

참깨빵위에참깨빵 2022. 2. 20. 17:45
728x90
반응형

서버에서 API를 통해 JSON 값들을 내려받으면 클라이언트인 안드로이드는 이를 파싱해서 필요한 곳에 알맞게 값들을 넣어주거나 표시해야 한다.

그러나 매번 JSONObject, JSONArray를 일일이 선언해서 파싱할 수는 없다. 귀찮다.

이 귀찮음을 해소하기 위해 존재하는 라이브러리가 바로 Gson이라는 라이브러리다. 이걸 쓰면 귀찮아서 숨 넘어갈 것 같은 JSON 파싱 과정을 매우 많이 단축시켜준다.

주의할 것은 JSON 파싱을 수작업으로 해본 적 없이 이 라이브러리를 먼저 사용하지 않는 것이다. 최소한 내가 받은 JSON이 어떤 구조인지 대충 짐작할 수 있고 로직이 어찌저찌 생각나는 정도가 되고 나서야 사용하면 좋다. 단순한 구조의 JSON이라면 Gson을 쓰는 것보다 수작업으로 파싱 때리는 게 더 나을 수도 있다.

각설하고, 먼저 Gson이란 무엇인지부터 알아보자. 구글에 검색해보면 공식 사이트처럼 보이는 곳이 몇 곳 있다.

 

https://github.com/google/gson

 

GitHub - google/gson: A Java serialization/deserialization library to convert Java Objects into JSON and back

A Java serialization/deserialization library to convert Java Objects into JSON and back - GitHub - google/gson: A Java serialization/deserialization library to convert Java Objects into JSON and back

github.com

Gson은 자바 객체를 JSON 표현으로 변환하는 데 사용할 수 있는 자바 라이브러리다. JSON 문자열을 동등한 자바 객체로 변환하는 데에도 사용할 수 있다. Gson은 소스코드가 없는 기존 객체를 포함해 임의의 자바 객체로 작업할 수 있다. 자바 객체를 JSON 변환할 수 있는 몇 가지 오픈소스 프로젝트가 있다. 그러나 대부분은 클래스에 자바 어노테이션을 배치해야 한다. 또한 대부분은 제네릭의 사용을 완전히 지원하지 않는다. Gson은 이 2가지 모두를 매우 중요한 디자인 목표로 간주한다.

 

정리하면 자바 객체 <-> JSON 변환할 때 사용할 수 있는 자바 라이브러리다. 메서드 설명에 좀 더 집중하는 자바독에선 Gson 클래스에 대해 아래와 같이 설명한다.

 

https://www.javadoc.io/doc/com.google.code.gson/gson/2.8.0/com/google/gson/Gson.html

 

Gson - gson 2.8.0 javadoc

Latest version of com.google.code.gson:gson https://javadoc.io/doc/com.google.code.gson/gson Current version 2.8.0 https://javadoc.io/doc/com.google.code.gson/gson/2.8.0 package-list path (used for javadoc generation -link option) https://javadoc.io/doc/co

www.javadoc.io

Gson은 일반적으로 먼저 Gson 인스턴스를 구성한 다음 toJson(Object) 또는 fromJson(String, Class)를 호출해 사용한다. Gson 인스턴스는 Thread-safe하므로 여러 쓰레드에서 자유롭게 재사용할 수 있다. 기본 구성이 필요한 모든 경우 new Gson()을 호출해 Gson 인스턴스를 만들 수 있다. GsonBuilder를 써서 버전 관리, 예쁜 인쇄, 사용자 지정 JsonSerializers, JsonDeserializers 및 InstanceCreators와 같은 다양한 구성 옵션으로 Gson 인스턴스를 빌드할 수도 있다.

 

Gson 객체를 만든 다음 toJson()과 fromJson()으로 JSON을 다루는 내용 같다. 이제 코드로 보자.

안드로이드 스튜디오를 사용한다면 먼저 아래의 의존성 문구를 앱 수준 gradle에 붙여넣는다. 레트로핏을 사용할 때 추가하는 의존성이다.

 

implementation 'com.squareup.retrofit2:converter-gson:2.3.0'

 

레트로핏과 상관없는 의존성은 아래와 같다. 위의 의존성이 작동하지 않는다면 이걸 써서 진행한다.

 

implementation 'com.google.code.gson:gson:2.9.0'

 

첫 번째 링크에 메이븐의 경우 어떻게 사용해야 하는지도 나와있으니 참고하자.

그 다음은 패키지명 옆에 (test)가 적힌 패키지 안에 테스트 파일을 작성하는 것이다. 에뮬레이터 없이도 실행이 가능하다.

 

import com.google.gson.Gson;

import org.junit.Before;
import org.junit.Test;

public class JsonTest {
    String json;
    Gson gson;

    @Before
    public void setBefore() {
        json = "{\n" +
                "\"name\":\"홍길동\",\n" +
                "\"age\":14,\n" +
                "\"height\":182.4,\n" +
                "\"marriage\":false\n" +
                "}";
        gson = new Gson();
    }

    @Test
    public void gsonTest() {
        Person person = gson.fromJson(json, Person.class);
        System.out.println("이름 : " + person.getName());
        System.out.println("나이 : " + person.getAge());
        System.out.println("키 : " + person.getHeight());
        System.out.println("결혼 여부 : " + person.isMarriage());
    }
}

 

여기선 json 변수 안에 담긴 값들을 하드코딩으로 때우지만 서버에서 레트로핏으로 값을 내려받는다면 response.body() 값을 담은 변수를 사용해도 된다.

다른 부분은 설명할 필요도 없으니 넘어가고 gsonTest() 안의 첫 코드를 보자.

gson.fromJson()의 첫 번째 매개변수로 String 형태의 JSON 값이 담긴 변수를 집어넣고, 그 다음으로 Person이란 클래스를 넣는다.

이 클래스는 아래와 같다.

 

import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;

public class Person {
    @SerializedName("name")
    @Expose
    private String personName;

    @SerializedName("age")
    @Expose
    private int age;

    @SerializedName("height")
    @Expose
    private double height;

    @SerializedName("marriage")
    @Expose
    private boolean marriage;

    public String getName() {
        return personName;
    }

    public int getAge() {
        return age;
    }

    public double getHeight() {
        return height;
    }

    public boolean isMarriage() {
        return marriage;
    }
}

 

값을 받아오기만 할 것이기 때문에 변수들과 getter가 있다. 중요한 것은 @SerializedName 이라는 어노테이션이다. 이 어노테이션은 어떤 역할을 하는 어노테이션일까?

 

https://howtodoinjava.com/gson/gson-serializedname/

 

Gson @SerializedName - Change field names example - HowToDoInJava

In this Gson @SerializedName example, learn to change the name of fields between json and java objects while serialization and deserialization process.

howtodoinjava.com

기본적으로 자바 모델 클래스와 JSON이 정확히 같은 필드명을 사용한다고 가정한다. 그러나 때로는 그렇지 않고 특정 이름이 다르다. 그 때 @SerializedName 어노테이션이 도움 된다. @SerializedName 어노테이션은 제공된 이름 값을 사용해서 JSON으로 직렬화돼야 함을 나타낸다. 이 어노테이션은 GsonBuilder 클래스를 사용했을 수 있는 기본 필드 명명 정책을 포함해 모든 FieldNamePolicy를 재정의한다. 이 어노테이션에 지정하는 값은 유효한 JSON 필드명이어야 한다.

 

쉽게 말해서 내가 선언한 DTO 안의 변수명이 JSON 안의 이름과 다를 경우, 어노테이션 안에 적은 이름의 JSON 값을 가져오는 어노테이션이다.

@SerializedName 어노테이션이 무슨 일을 하는지 더 명확하게 알기 위해 일단 실행시켜보자. 그럼 콘솔에 아래와 같은 결과가 출력된다.

 

원하는 대로 잘 파싱됐다. 이제 Person에서 name 위의 @SerializedName 어노테이션을 없애고 다시 빌드해보자.

Person은 이런 형태가 돼야 한다.

import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;

public class Person {
    @Expose
    private String personName;

    @SerializedName("age")
    @Expose
    private int age;

    @SerializedName("height")
    @Expose
    private double height;

    @SerializedName("marriage")
    @Expose
    private boolean marriage;

    public String getName() {
        return personName;
    }

    public int getAge() {
        return age;
    }

    public double getHeight() {
        return height;
    }

    public boolean isMarriage() {
        return marriage;
    }
}

 

이름을 받아오지 못하고 null이 출력된다. 그럼 age 위의 @SerializedName 어노테이션을 지워도 나이값을 받아오지 못하는 건가?

import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;

public class Person {
    @Expose
    private String personName;

    @Expose
    private int age;

    @SerializedName("height")
    @Expose
    private double height;

    @SerializedName("marriage")
    @Expose
    private boolean marriage;

    public String getName() {
        return personName;
    }

    public int getAge() {
        return age;
    }

    public double getHeight() {
        return height;
    }

    public boolean isMarriage() {
        return marriage;
    }
}

 

아니다. age위의 @SerializedName 어노테이션을 지워도 나이는 잘 받아온다.

이걸 보면 @SerializedName 어노테이션이 하는 일이 더 명확해진다. Gson이 Person.class 안에 있는 변수명과 일치하는 JSON 값을 가져오는데 @SerializedName 어노테이션이 없고 변수명이 JSONObject의 key와 다를 경우, 그것과 일치하는 값을 변수와 매핑하지 못해 null을 출력하는 것이다.

그러니 가급적이면 JSON과 일치하는 자료형과 이름으로 변수를 선언하는 게 좋다. 이제 Person 클래스를 원래 상태로 돌려보자. personName이란 변수명도 원래는 name 이었다.

 

import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;

public class Person {
    @SerializedName("name")
    @Expose
    private String name;

    @SerializedName("age")
    @Expose
    private int age;

    @SerializedName("height")
    @Expose
    private double height;

    @SerializedName("marriage")
    @Expose
    private boolean marriage;

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public double getHeight() {
        return height;
    }

    public boolean isMarriage() {
        return marriage;
    }
}

 

다시 실행하면 잘 파싱해온다. 이런 식으로 JSONArray도 파싱해오면 된다.

반응형
Comments