본문 바로가기
(2024-10) 스파르타 내일배움캠프 - 백엔드/Java Reflect

필드 제어 예제2) Json Serializer with Array

by 어뫄어뫄 2024. 11. 27.

JsonSerializerDemo.java

package jsonserializer;

import models.Actor;
import models.Movie;

import java.lang.reflect.Array;
import java.lang.reflect.Field;

public class JsonSerializerDemo {
    public static void main(String[] args) throws IllegalAccessException {
        Actor actor1 = new Actor("Chris Hemsworth", new String[]{"Thor", "Extraction"});
        Actor actor2 = new Actor("Scarlett Johansson", new String[]{"Avengers", "Lucy"});
        Actor actor3 = new Actor("Robert Downey Jr.", new String[]{"Iron Man", "Sherlock Holmes"});

        Movie movie = new Movie("Avengers: Endgame", 8.4f, new String[]{"Action", "Sci-Fi", "Adventure"}, 
                                new Actor[]{actor1, actor2, actor3});

        String jsonOutput = serializeToJson(movie, 0);

        System.out.println(jsonOutput);
    }

    public static String serializeToJson(Object obj, int indentLevel) throws IllegalAccessException {
        Field[] fields = obj.getClass().getDeclaredFields();
        StringBuilder jsonBuilder = new StringBuilder();

        jsonBuilder.append(addIndentation(indentLevel)).append("{").append("\n");

        for (int i = 0; i < fields.length; i++) {
            Field field = fields[i];
            field.setAccessible(true);

            if (field.isSynthetic()) {
                continue;
            }

            jsonBuilder.append(addIndentation(indentLevel + 1))
                       .append(formatString(field.getName()))
                       .append(": ");

            if (field.getType().isPrimitive()) {
                jsonBuilder.append(formatPrimitiveValue(field.get(obj), field.getType()));
            } else if (field.getType().equals(String.class)) {
                jsonBuilder.append(formatString((String) field.get(obj)));
            } else if (field.getType().isArray()) {
                jsonBuilder.append(convertArrayToJson(field.get(obj), indentLevel + 1));
            } else {
                jsonBuilder.append(serializeToJson(field.get(obj), indentLevel + 1));
            }

            if (i < fields.length - 1) {
                jsonBuilder.append(",");
            }
            jsonBuilder.append("\n");
        }

        jsonBuilder.append(addIndentation(indentLevel)).append("}");
        return jsonBuilder.toString();
    }

    private static String convertArrayToJson(Object arrayObj, int indentLevel) throws IllegalAccessException {
        StringBuilder jsonBuilder = new StringBuilder();

        int length = Array.getLength(arrayObj);
        Class<?> componentType = arrayObj.getClass().getComponentType();

        jsonBuilder.append("[\n");

        for (int i = 0; i < length; i++) {
            Object element = Array.get(arrayObj, i);

            if (componentType.isPrimitive()) {
                jsonBuilder.append(addIndentation(indentLevel + 1))
                           .append(formatPrimitiveValue(element, componentType));
            } else if (componentType.equals(String.class)) {
                jsonBuilder.append(addIndentation(indentLevel + 1))
                           .append(formatString((String) element));
            } else {
                jsonBuilder.append(serializeToJson(element, indentLevel + 1));
            }

            if (i < length - 1) {
                jsonBuilder.append(",");
            }
            jsonBuilder.append("\n");
        }

        jsonBuilder.append(addIndentation(indentLevel)).append("]");
        return jsonBuilder.toString();
    }

    private static String addIndentation(int level) {
        return "\t".repeat(level);
    }

    private static String formatPrimitiveValue(Object value, Class<?> type) {
        if (type.equals(boolean.class) || type.equals(int.class) || type.equals(short.class)) {
            return value.toString();
        } else if (type.equals(float.class) || type.equals(double.class)) {
            return String.format("%.2f", value);
        }

        throw new UnsupportedOperationException("Unsupported data type: " + type.getName());
    }

    private static String formatString(String value) {
        return "\"" + value + "\"";
    }
}

 

models/Actor.java

package models;

public class Actor {
    private final String name;
    private final String[] notableMovies;

    public Actor(String name, String[] notableMovies) {
        this.name = name;
        this.notableMovies = notableMovies;
    }
}

 

models/Movie.java

package models;

public class Movie {
    private final String title;
    private final float imdbRating;
    private final String[] genres;
    private final Actor[] cast;

    public Movie(String title, float imdbRating, String[] genres, Actor[] cast) {
        this.title = title;
        this.imdbRating = imdbRating;
        this.genres = genres;
        this.cast = cast;
    }
}

 

 

1. Array.getLength(Object array)

  • 설명: 배열 객체의 길이를 반환합니다.
  • 사용 예:
     
    int length = Array.getLength(arrayObj);
    arrayObj 배열의 길이를 가져와 JSON 직렬화 과정에서 반복 처리를 위한 기준으로 사용합니다.

2. Array.get(Object array, int index)

  • 설명: 배열의 특정 인덱스에 있는 요소를 가져옵니다.
  • 사용 예:
    Object element = Array.get(arrayObj, i);
    arrayObj 배열의 i번째 요소를 가져와 타입에 따라 JSON 직렬화 방식을 결정합니다.

3. Class.isArray()

  • 설명: 특정 클래스 객체가 배열인지 확인합니다.
  • 사용 예:
    if (field.getType().isArray()) { jsonBuilder.append(convertArrayToJson(field.get(obj), indentLevel + 1)); }
    필드 타입이 배열인지 검사하여 배열 처리 로직(convertArrayToJson)으로 분기합니다.

4. Class.getComponentType()

  • 설명: 배열의 요소 타입(컴포넌트 타입)을 반환합니다.
  • 사용 예:
    Class<?> componentType = arrayObj.getClass().getComponentType();
     
    배열 요소의 데이터 타입을 확인하여 각 요소를 JSON으로 변환하는 방식을 결정합니다.

5. Class.getDeclaredFields() 및 Field.setAccessible(true)

  • 설명: 이전 예제와 동일하게 객체의 필드를 가져오고 접근 제한을 해제하여 값을 읽습니다.
  • 사용 예:
    Field[] fields = obj.getClass().getDeclaredFields(); field.setAccessible(true);
     

예제 코드 분석

1. 배열 처리 로직 (convertArrayToJson)

  • 배열 직렬화를 위해 Array.getLength와 Array.get을 사용합니다.
  • 요소 타입에 따라 분기:
    • 기본형(Primitive): formatPrimitiveValue를 호출.
    • 문자열(String): JSON 문자열로 포맷팅.
    • 복합 객체(Object): 재귀적으로 serializeToJson 호출.
int length = Array.getLength(arrayObj);
for (int i = 0; i < length; i++) {
    Object element = Array.get(arrayObj, i);

    if (componentType.isPrimitive()) {
        jsonBuilder.append(formatPrimitiveValue(element, componentType));
    } else if (componentType.equals(String.class)) {
        jsonBuilder.append(formatString((String) element));
    } else {
        jsonBuilder.append(serializeToJson(element, indentLevel + 1));
    }
}

2. JSON 직렬화 과정

  1. 필드 접근 및 데이터 타입 확인:
    • 배열인 경우, 배열 처리 메서드 convertArrayToJson으로 위임.
       
       
      if (field.getType().isArray()) {
          jsonBuilder.append(convertArrayToJson(field.get(obj), indentLevel + 1));
      }
  2. 출력 예: Movie 객체를 직렬화하면 다음 JSON 결과가 생성됩니다:

 

{
    "title": "Avengers: Endgame",
    "imdbRating": 8.40,
    "genres": [
        "Action",
        "Sci-Fi",
        "Adventure"
    ],
    "cast": [
        {
            "name": "Chris Hemsworth",
            "notableMovies": [
                "Thor",
                "Extraction"
            ]
        },
        {
            "name": "Scarlett Johansson",
            "notableMovies": [
                "Avengers",
                "Lucy"
            ]
        },
        {
            "name": "Robert Downey Jr.",
            "notableMovies": [
                "Iron Man",
                "Sherlock Holmes"
            ]
        }
    ]
}

'(2024-10) 스파르타 내일배움캠프 - 백엔드 > Java Reflect' 카테고리의 다른 글

Getter 동적 생성  (0) 2024.11.27
ConfigLoader 예제  (0) 2024.11.27
Field 제어  (0) 2024.11.27
Java Reflect의 Array 제어  (0) 2024.11.27
Constructor 예제, 싱글턴에서(2)  (0) 2024.11.26