## 35. JSON 형식으로 입출력하기
- JSON 형식으로 데이터를 읽고 쓰는 법
- Gson 라이브러리 사용법
JSON 형식으로 입출력하기
ㄴ JSON 형식으로 데이터를 읽고 쓰는 법
ㄴ Gson 라이브러리 사용법
build.gradle
ㄴ build.gradle 스크립트 파일에 Google JSON 라이브러리 추가하기
ㄴ Gradle 재설정하기
ㄴ 프로젝트 Refresh
ㄴ References Libraries 에 gson-2.10.1.jar 이 존재하는지 확인
SaveJson
App.java
ㄴ saveCsv -> saveJson 으로 변경
App.java
ㄴ 제네릭 ? extends CsvObject -> ? 로 변경
App.java
ㄴ PrintWriter 제거해도 됨 (out1 -> out 으로 변경)
App.java
ㄴ 해당 코드도 필요 없음
App.java
ㄴ GsonBuilder를 생성하고, setDateFormat 메서드를 사용하여 날짜 형식을 "yyyy-MM-dd" 로 지정한 후, create 메서드를 호출하여 Gson 객체를 생성
=> 생성된 Gson 객체는 날짜를 지정한 형식으로 직렬화 및 역직렬화할 수 있음
App.java
ㄴ 레퍼런스 변수 gson 에 Gson 객체의 주소를 저장
App.java
ㄴ list 객체를 JSON 형식의 문자열로 변환하여 out.write()를 통해 출력
App.java
ㄴ saveCsv -> saveJson 으로 변경
ㄴ 파일 확장자도 .csv -> .json 으로 변경
App.java
ㄴ setPrettyPrinting() 을 이용해 Gson이 생성한 JSON 문자열을 읽기 쉽고 보기 좋게 형식화해줌
=> 생성된 JSON 문자열은 들여쓰기와 줄 바꿈이 적용되어 가독성이 높아짐
App 클래스 실행
ㄴ .json 파일들이 각각 생성됨
Text Editor 이용해서 .json 파일 확인
board.json
LoadJson
App.java
ㄴ loadCsv -> loadJson 으로 변경
App.java
ㄴ T extends CsvObject -> T 로 변경
App.java
ㄴ 해당 코드 필요 없으므로 제거
App.java
ㄴ StringBuilder 이용
App.java
ㄴ 해당 코드 필요 없으므로 제거
App.java
ㄴ 넘어온 filename 을 FileReader의 생성자에 전달하여 FileReader 객체를 생성한 후 BufferedReader의 생성자에 FileReader 객체를 전달하여 BufferedReader로 감싸어 버퍼링된 입력을 제공함
ㄴ 그 후 readLine() 메서드를 호출하여 한 줄씩 읽어옴
ㄴ 읽어온 각 줄은 strBuilder.append(line)을 사용하여 StringBuilder에 추가됨
=> 이렇게 계속해서 줄을 읽어와서 문자열을 빌드하면 strBuilder에 최종적으로 완성된 문자열이 저장됨
App.java
ㄴ loadCsv -> loadJson 으로 변경
ㄴ 파일 확장자 .csv -> .json 으로 변경
App 클래스 실행
=>
ㄴ strBuilder 에 잘 저장되었는지 출력하여 확인
App.java
ㄴ GsonBuilder 클래스를 사용하여 Gson 객체를 생성하고, 날짜 형식을 설정
App.java
ㄴ java.lang.reflect.Type 인터페이스 import
ㄴ TypeToken.getParameterized() 메서드를 호출하여 Collection 클래스의 제네릭 타입을 지정
ㄴ 두 번째 인자로는 실제 요소 타입인 clazz를 전달
ㄴ getType() 메서드를 호출하여 최종적으로 Type 객체를 얻음
App.java
ㄴ 첫 번째 인자로는 JSON 문자열인 strBuilder.toString()을 전달하고, 두 번째 인자로는 역직렬화할 타입인 type을 전달
=> JSON 문자열이 Collection<T> 타입으로 역직렬화되어 해당 컬렉션 객체인 objects에 저장됨
ㄴ gson.fromJson() 메서드를 사용하여 strBuilder의 JSON 문자열을 파싱하고, 타입 T의 객체 컬렉션으로 역직렬화
ㄴ type 매개변수는 역직렬화의 대상 타입을 지정함
App.java
ㄴ objects 컬렉션의 모든 요소를 list에 추가
=>
App.java
ㄴ 쓸데없는 변수는 추가하지 말자!
App.java
ㄴ 필요 없으므로 제거
ㄴ vo 패키지에 AutoIncrement 라는 이름으로 인터페이스 추가
AutoIncrement.java
ㄴ 자동 증가된 번호를 key로 사용하는 경우에 필요한 인터페이스 작성
Board.java
ㄴ Board, Member, Item 클래스에서는 자동 증가된 번호를 key 로 사용하므로 AutoIncrement 인터페이스 구현
Board.java
ㄴ Board, Member, Item 클래스에서 AutoIncrement 인터페이스에 따른 updateKey(int no) 구현
App.java
ㄴ clazz가 구현하고 있는 인터페이스 중에서 AutoIncrement 인터페이스를 찾도록 함
ㄴ clazz의 인터페이스들을 확인하면서 AutoIncrement 인터페이스를 찾으면 "오호라 당첨!"이라는 메시지를 출력
=> clazz가 AutoIncrement 인터페이스를 구현하고 있는지 확인할 수 있음
=>
App.java
ㄴ list의 마지막 요소를 가져와서 AutoIncrement 인터페이스로 형변환
ㄴ list의 마지막 요소를 가져오고, 이 요소가 AutoIncrement 인터페이스를 구현하고 있다고 가정하고 형변환을 수행
ㄴ 형변환된 객체를 autoIncrement 변수에 할당
=> list의 마지막 요소를 AutoIncrement 인터페이스로 사용하여 updateKey() 실행
=> list에 저장된 객체들 중에서 마지막으로 추가된 객체가 AutoIncrement 인터페이스를 구현한 경우에만 작동
=> clazz가 구현한 인터페이스들을 가져와서 순회하면서 AutoIncrement 인터페이스와 비교하고, 일치하는 경우에 마지막으로 추가된 객체의 updateKey() 메서드를 호출
ㄴ clazz가 AutoIncrement 인터페이스를 구현한 경우에만 키를 업데이트
(clazz가 AutoIncrement 인터페이스를 직접 구현하지 않고, 해당 인터페이스를 상속받은 클래스를 통해 구현한 경우에는 getInterfaces() 메서드는 해당 인터페이스를 반환하지 않을 수 있음. 이 경우에는 getInterfaces() 메서드로 가져오는 인터페이스 목록에 AutoIncrement 인터페이스가 포함되지 않으므로, 해당 조건문에서 걸러지게 됨.)
AutoIncrement.java
=>
AutoIncrement.java
ㄴ int no 제거
Board.java
ㄴ AutoIncrement 인터페이스에서 in no 제거했으므로 App 클래스에 구현한 updateKey 메서드에서도 제거
ㄴ no -> this.no 로 변경
=> this.no 가 더 크다면 boardNo 를 증가 시켜주기
=> Member, Item 도 동일하게 구현해주기
App.java
=>
ㄴ 확인용으로 사용했던 코드 제거
Board.java
=>
Board.java
ㄴ 기본 생성자에서 no 를 증가해주지 않도록 해당 코드 제거
ㄴ 기본 생성자를 아예 제거하면 안 됨
=> 기본 생성자를 제외한 다른 생성자가 하나라도 있으면 컴파일러가 기본 생성자를 생성하지 않기 때문 (기본 생성자는 필수)
BoardAddListener.java
ㄴ BoardNo 를 BoardAddListener 에서 증가시켜주기
ㄴ Member, Item 도 동일하게 적용해주기
BoardAddListener.java
ㄴ Board 에 있는 CreatedDate 도 원래 생성자에서 설정해줬는데 BoardAddListener 에서 설정해주기