본문 바로가기
카테고리 없음

필드 제어 예제) JsonSerializer

by 어뫄어뫄 2024. 11. 27.

JsonSerializer.java

package jsonserializer;

import models.Address;
import models.Company;
import models.Person;

import java.lang.reflect.Field;

public class JsonSerializer {
    public static void main(String[] args) throws IllegalAccessException {
        Company company = new Company("TechCorp", "Seattle", new Address("Pine Street", (short) 800));
        Address address = new Address("Broadway", (short) 10);
        Person person = new Person("Alice", true, 32, 1200.75f, address, company);

        String jsonOutput = serializeToJson(person, 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(getPrimitiveValue(field, obj));
            } else if (field.getType().equals(String.class)) {
                jsonBuilder.append(formatString((String) field.get(obj)));
            } 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 addIndentation(int level) {
        return "\t".repeat(level);
    }

    private static String getPrimitiveValue(Field field, Object obj) throws IllegalAccessException {
        if (field.getType().equals(boolean.class) || field.getType().equals(int.class) || field.getType().equals(short.class)) {
            return field.get(obj).toString();
        } else if (field.getType().equals(float.class) || field.getType().equals(double.class)) {
            return String.format("%.2f", field.get(obj));
        }
        throw new UnsupportedOperationException("Unsupported data type: " + field.getType().getName());
    }

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

 

models/Address.java

package models;

public class Address {
    private final String street;
    private final short apartment;

    public Address(String street, short apartment) {
        this.street = street;
        this.apartment = apartment;
    }
}

 

models/Company.java

package models;

public class Company {
    private final String name;
    private final String city;
    private final Address address;

    public Company(String name, String city, Address address) {
        this.name = name;
        this.city = city;
        this.address = address;
    }
}

 

models/Person.java

package models;

public class Person {
    private final String name;
    private final boolean employed;
    private final int age;
    private final float salary;
    private final Address address;
    private final Company job;

    public Person(String name, boolean employed, int age, float salary, Address address, Company job) {
        this.name = name;
        this.employed = employed;
        this.age = age;
        this.salary = salary;
        this.address = address;
        this.job = job;
    }
}

 

 

1. Class.getDeclaredFields()

  • 설명: 해당 클래스에 선언된 모든 필드를 가져옵니다(접근 제한자와 관계없이).
  • 사용 예:
    Field[] fields = obj.getClass().getDeclaredFields();
     
    obj 객체의 클래스에서 선언된 필드 목록을 배열로 가져옵니다. 이 필드를 활용해 객체의 속성 값에 접근합니다.

2. Field.setAccessible(true)

  • 설명: 필드가 private이나 protected로 선언된 경우에도 접근 가능하도록 설정합니다.
  • 사용 예:
    field.setAccessible(true);
    객체 직렬화를 위해 private 필드도 접근 가능하도록 허용합니다.

3. Field.get(Object obj)

  • 설명: 특정 객체에서 해당 필드의 값을 가져옵니다.
  • 사용 예:
    Object value = field.get(obj);
    현재 순회 중인 필드에서 객체 obj의 실제 값을 얻습니다.

4. Field.getType()

  • 설명: 필드의 데이터 타입(Class 객체)을 반환합니다.
  • 사용 예:
    if (field.getType().equals(String.class)) { // 문자열 처리 }
    필드의 데이터 타입을 검사하여 적절한 JSON 직렬화 방식을 결정합니다.

5. Field.isSynthetic()

  • 설명: 컴파일러가 생성한 필드인지 확인합니다.
  • 사용 예:
    if (field.isSynthetic()) { continue; }
    직렬화 과정에서 컴파일러가 추가한 필드를 무시합니다.

예제 코드 분석

JSON 직렬화 과정

  1. 필드 탐색 및 접근
    • obj의 클래스에 선언된 모든 필드에 대해 Reflection을 사용해 값을 읽습니다.
       
      Field[] fields = obj.getClass().getDeclaredFields();
      field.setAccessible(true);
  2. 데이터 타입에 따른 처리
    • 기본형(Primitive), 문자열(String), 복합 객체(Object)로 분기하여 JSON 직렬화 방식을 달리합니다.

      if (field.getType().isPrimitive()) {
          jsonBuilder.append(getPrimitiveValue(field, obj));
      } else if (field.getType().equals(String.class)) {
          jsonBuilder.append(formatString((String) field.get(obj)));
      } else {
          jsonBuilder.append(serializeToJson(field.get(obj), indentLevel + 1));
      }
  3. 출력 예 Person 객체를 직렬화하면 다음과 같은 JSON 출력이 생성됩니다:
     
    { "name": "Alice", "employed": true, "age": 32, "salary": 1200.75, "address": { "street": "Broadway", "apartment": 10 }, "job": { "name": "TechCorp", "city": "Seattle", "address": { "street": "Pine Street", "apartment": 800 } } }