Spring Boot

[Spring Boot] JSON String , Object 변환 (JSONParser, Gson, object-mapper)

공대생안씨 2024. 3. 16. 15:42

2024.03.15 - [Spring Boot] - [Spring Boot] JSON 객체 송수신 예제

 

[Spring Boot] JSON 객체 송수신 예제

1. JSON 이란 JSON : JavaScript Object Notation 데이터 저장, 전송 등에 사용되는 데이터 교환 형식 서버와 클라이언트 사이의 데이터 교환에 주로 사용됨 키 : 값 형태를 가짐 ex : "name": "홍길동" 2. 기본 클

blogan99.tistory.com

위의 게시물과 내용이 이어짐

 

1. JsonString 를 Object 로, Object 를 JsonString 으로 변환 방법

  • Json 형태로 입력받은 String을 Object로 변환
  • 입력받은 Objcet 를 Json 형태의 String으로 변환
  • JSONParser, Gson, object-mapper 의 3가지 방법으로 변환함 

 

 

2. JSONParser 를 이용한 JsonString <-> Object 변환

  • JSONObject 내부적으로 데이터 저장 시 HashMap 등의 자료구조를 사용함

 

2-1. build.gradle (라이브러리 추가)

dependencies {
    //JSONParser
    implementation 'com.googlecode.json-simple:json-simple:1.1.1'
}

 

2-2. JsonString -> Object 변환

@RestController
@RequestMapping("/json-parser")
public class JsonParserController {

    @PostMapping("/string-to-student")
    public StudentDto stringToStudent(@RequestBody String inputString) throws ParseException {

        JSONParser jsonParser = new JSONParser();
        JSONObject jsonObject = (JSONObject) jsonParser.parse(inputString);

        // Student 객체 생성
        Student student = new Student(jsonObject.get("name").toString(),
                jsonObject.get("password").toString(),
                Integer.valueOf(jsonObject.get("age").toString()),
                (ArrayList<String>) jsonObject.get("classes"));

        // Student 객체를 StudentDTO로 변환
        StudentDto studentDto = new StudentDto(student);

        return studentDto;
    }
}

 

  • 실행 결과

 

2-3. Object -> JsonString 변환

@PostMapping("/student-to-string")
public String studentToString(@RequestBody Student student) {
    JSONObject jsonObject = new JSONObject();
    jsonObject.put("name", student.getName());

    // 비밀번호는 담지 않는다고 가정
    // jsonObject.put("password", student.getPassword());

    jsonObject.put("age", student.getAge());
    jsonObject.put("classes", student.getClasses());

    return jsonObject.toJSONString();
}

 

  • 실행 결과

 

 

3. Gson 을 이용한 JsonString <-> Object 변환

  • JSON 스펙 자체에서 데이터의 순서를 보장하지 않음
  • JSONParser 를 사용하면 위의 결과처럼 데이터의 순서 보장 x
  • 데이터의 순서를 보장하고 싶다면 Gson을 사용하는 것도 좋은 방법임
  • Gson 라이브러리의 JsonObject 는 내부적으로 LinkedTreeMap 의 자료구조를 사용함
    • 추가한 데이터의 순서대로 데이터가 저장됨

 

3-1. build.gradle (라이브러리 추가)

dependencies {
    // Gson
    implementation 'com.google.code.gson:gson:2.8.6'
}

 

3-2. JsonString -> Object 변환

@RestController
@RequestMapping("/gson")
public class GsonController {

    @PostMapping("/string-to-student")
    public StudentDto stringToStudent(@RequestBody String inputString) {

        Gson gson = new Gson();
        Student student = gson.fromJson(inputString, Student.class);

        // Student 를 StudentDTO로 변환해서 리턴
        return new StudentDto(student);
    }
}

 

  • 실행 결과

 

3-3. Object -> JsonString 변환

@PostMapping("/student-to-string")
public String studentToString(@RequestBody Student student) {
    Gson gson = new Gson();
    String jsonString = gson.toJson(student);

    return jsonString;
}

 

  • 실행 결과

  • gson.toString 을 통해서 JsonString으로 변환
    • 여기서 password가 그대로 노출!

 

3-4. Object -> JsonString 변환 + 제외 필드 추가 (password 제외)

 

3-4-1. @Expose 사용

  • Student 클래스에 JsonString으로 변환하고 싶은 (노출하고 싶은) 필드에 @Expose 어노테이션 추가함
@Getter
@Setter
@AllArgsConstructor
public class Student {

    @Expose
    public String name;
    public String password;
    @Expose
    public int age;

    @Expose
    public ArrayList<String> classes = new ArrayList<>();

    public Student() {
    }
}

 

  • RestController 수정
@PostMapping("/student-to-string-expose")
public String studentToString2(@RequestBody Student student) {
    Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
    String jsonString = gson.toJson(student);

    return jsonString;
}

 

  • 실행 결과

  • password 가 제외됨을 확인!

 

3-4-2. Gson의 ExclusionStrategy 사용

  • ExclusionStrategy 인터페이스 구현
static class PasswordExclusionStrategy implements ExclusionStrategy {
    @Override
    public boolean shouldSkipField(FieldAttributes f) {
        // password 필드를 스킵함
        return f.getName().equals("password");
    }

    @Override
    public boolean shouldSkipClass(Class<?> clazz) {
        // 스킵할 클래스는 없으므로 false 리턴
        return false;
    }
}

 

  • RestController 수정
@PostMapping("/student-to-string-exclusion-strategy")
public String studentToString3(@RequestBody Student student) {
    Gson gson = new GsonBuilder()
            .setExclusionStrategies(new PasswordExclusionStrategy())
            .create();
    String jsonString = gson.toJson(student);

    return jsonString;
}

 

  • 실행 결과

  • password가 제외됨을 확인!
    • 제외 전략 (Exclusion Strategy) 에 해당하는 클래스를 새로 만듦으로써 다른 클래스(ex. Student) 혹은 엔티티 등을 수정하지 않고 해당 api 스펙에 맞게끔 제외 필드를 설정할 수 있음!

 

 

4. object-mapper 를 이용한 JsonString <-> Object 변환

 

4-1. JsonString -> Object 변환

@RestController
@RequestMapping("/object-mapper")
public class ObjectMapperController {

    @PostMapping("/string-to-student")
    public StudentDto stringToStudent(@RequestBody String inputString) throws JsonProcessingException {

        ObjectMapper objectMapper = new ObjectMapper();

        Map<String, Object> map = objectMapper.readValue(inputString, new TypeReference<>() {});
        Student student = new Student(map.get("name").toString(),
                map.get("password").toString(),
                Integer.valueOf(map.get("age").toString()),
                (ArrayList<String>) map.get("classes"));

        return new StudentDto(student);
    }
}

 

  • 실행 결과

 

4-2. Object -> JsonString 변환

@PostMapping("/student-to-string")
public String studentToString(@RequestBody Student student) throws JsonProcessingException {
    ObjectMapper objectMapper = new ObjectMapper();

    String result = objectMapper.writeValueAsString(student);
        
    return result;
}

 

  • 실행 결과

  • objectMapper.writeValueAsString(student) 로 바로 JsonString으로 변환
    • password가 그대로 노출!

 

4-3. Object -> JsonString 변환 + 제외 필드 추가 (password 제외)

 

4-3-1. @JsonIgnore 사용

  • Student 클래스에서 제외하고 싶은 필드에 @JsonIgnore 어노테이션 추가
@Getter
@Setter
@AllArgsConstructor
public class Student {

    public String name;
    
    @JsonIgnore    // password 필드 제외
    public String password;
    public int age;

    public ArrayList<String> classes = new ArrayList<>();

    public Student() {
    }
}

 

  • 실행 결과

  • password가 제외됨을 확인!

 

4-3-2. ObjectMapper 커스터마이징

  • Student 클래스를 수정할 수 없는 상황일 때 특정 필드 제외 가능
  • 엔티티 등을 api 스펙에 맞춰서 수정하지 않고 특정 필드를 제외할 수 있기 때문에 가용성이 좋음

 

  • Student 클래스에 @JsonFilter 어노테이션 추가
@Getter
@Setter
@AllArgsConstructor
@JsonFilter("studentFilter")	// studentFilter 적용
public class Student {

    public String name;
//    @JsonIgnore
    public String password;
    public int age;

    public ArrayList<String> classes = new ArrayList<>();

    public Student() {
    }
}

 

  • RestController 수정
@PostMapping("/student-to-string-filter")
public String studentToString2(@RequestBody Student student) throws JsonProcessingException {
    ObjectMapper objectMapper = new ObjectMapper();

    // studentFilter : password 필드 제외 한 모든 필드를 직렬화
    SimpleFilterProvider filter = new SimpleFilterProvider().addFilter("studentFilter",
            SimpleBeanPropertyFilter.serializeAllExcept("password"));

    objectMapper.setFilterProvider(filter);

    String result = objectMapper.writeValueAsString(student);

    return result;
}

 

  • 실행 결과

  • password 가 제외됨을 확인!