programing

Jackson JSON 역직렬화(복수의 파라미터 컨스트럭터 사용)

mytipbox 2023. 3. 14. 21:19
반응형

Jackson JSON 역직렬화(복수의 파라미터 컨스트럭터 사용)

저는 한동안 프로젝트에서 FasterXML/Jackson-Databind를 사용해 왔습니다.이 을 발견하고 이 접근방식을 사용하여 @JsonProperty 주석 없이 오브젝트를 역직렬화하기 시작했습니다.

문제는 여러 파라미터를 사용하여 이 컨스트럭터를 @JsonCreator 주석으로 장식하는 컨스트럭터가 있을 때 잭슨이 다음 오류를 발생시킨다는 것입니다.

Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: 
Argument #0 of constructor [constructor for com.eliti.model.Cruiser, annotations: {interface com.fasterxml.jackson.annotation.JsonCreator=@com.fasterxml.jackson.annotation.JsonCreator(mode=DEFAULT)}] has no property name annotation; must have name when multiple-parameter constructor annotated as Creator
 at [Source: {
  "class" : "com.eliti.model.Cruiser",
  "inventor" : "afoaisf",
  "type" : "MeansTransport",
  "capacity" : 123,
  "maxSpeed" : 100
}; line: 1, column: 1]

이 문제를 설명하기 위해 작은 프로젝트를 만들었습니다. 제가 디세럴라이즈하려는 클래스는 다음과 같습니다.

public class Cruise extends WaterVehicle {

 private Integer maxSpeed;

  @JsonCreator
  public Cruise(String name, Integer maxSpeed) {
    super(name);
    System.out.println("Cruise.Cruise");
    this.maxSpeed = maxSpeed;
  }

  public Integer getMaxSpeed() {
    return maxSpeed;
  }

  public void setMaxSpeed(Integer maxSpeed) {
    this.maxSpeed = maxSpeed;
  }

}

deserialize 코드는 다음과 같습니다.

public class Test {
  public static void main(String[] args) throws IOException {
    Cruise cruise = new Cruise("asd", 100);
    cruise.setMaxSpeed(100);
    cruise.setCapacity(123);
    cruise.setInventor("afoaisf");

    ObjectMapper mapper = new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT);
    mapper.registerModule(new ParameterNamesModule(JsonCreator.Mode.PROPERTIES));

    String cruiseJson = mapper.writeValueAsString(cruise);

    System.out.println(cruiseJson);

    System.out.println(mapper.readValue(cruiseJson, Cruise.class));

}

이미 @JsonCreator를 삭제하려고 했는데 삭제하면 다음 예외가 발생합니다.

Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of com.eliti.model.Cruise: no suitable constructor found, can not deserialize from Object value (missing default constructor or creator, or perhaps need to add/enable type information?)
 at [Source: {
  "class" : "com.eliti.model.Cruise",
  "inventor" : "afoaisf",
  "type" : "MeansTransport",
  "capacity" : 123,
  "maxSpeed" : 100
}; line: 3, column: 3]

"mvn 클린 설치"를 실행하려고 했지만 아직 문제가 발생.

이 문제에 대해 자세히 조사했습니다(GitHub 문제, 블로그 투고, StackOverflow Q&A).다음은 제 쪽에서 수행한 디버깅/조사입니다.

조사 1

생성된 바이트 코드의 javap -v는 다음과 같습니다.

 MethodParameters:
      Name                           Flags
      name
      maxSpeed

컨스트럭터라고 하면 -parameters 플래그가 javac 컴파일러용으로 설정되어 있는 것 같습니다.

조사 2

단일 매개 변수를 사용하여 생성자를 만들면 개체는 초기화되지만 다중 매개 변수 생성자를 사용해야 합니다.

조사 3

각 필드에서 주석 @JsonProperty를 사용하면 동작하지만 원래 프로젝트의 경우 컨스트럭터에 필드가 많기 때문에 오버헤드가 너무 큽니다(또한 주석으로 코드를 리팩터링하는 것도 매우 어렵습니다).

남은 질문은 다음과 같습니다.주석 없이 Jackson이 여러 매개 변수 생성자를 사용하여 작업하도록 하려면 어떻게 해야 합니까?

주석을 추가해야 합니다.@JsonProperty개체를 만들 때 생성자에게 전달해야 하는 json 속성의 이름을 지정합니다.

public class Cruise extends WaterVehicle {

 private Integer maxSpeed;

  @JsonCreator
  public Cruise(@JsonProperty("name") String name, @JsonProperty("maxSpeed")Integer maxSpeed) {
    super(name);
    System.out.println("Cruise.Cruise");
    this.maxSpeed = maxSpeed;
  }

  public Integer getMaxSpeed() {
    return maxSpeed;
  }

  public void setMaxSpeed(Integer maxSpeed) {
    this.maxSpeed = maxSpeed;
  }

}

편집

방금 아래 코드를 사용하여 테스트했는데, 문제 없습니다.

import java.io.IOException;

import com.fasterxml.jackson.annotation.JsonCreator.Mode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;

class WaterVehicle {

    private String name;
    private int capacity;
    private String inventor;
    public WaterVehicle(String name) {
        this.name=name;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getCapacity() {
        return capacity;
    }
    public void setCapacity(int capacity) {
        this.capacity = capacity;
    }
    public String getInventor() {
        return inventor;
    }
    public void setInventor(String inventor) {
        this.inventor = inventor;
    }


}

 class Cruise  extends WaterVehicle{

        private Integer maxSpeed;

        public Cruise(String name, Integer maxSpeed) {
            super(name);
            this.maxSpeed = maxSpeed;
        }

        public Integer getMaxSpeed() {
            return maxSpeed;
        }

        public void setMaxSpeed(Integer maxSpeed) {
            this.maxSpeed = maxSpeed;
        }


    }

public class Test {
      public static void main(String[] args) throws IOException {
        Cruise cruise = new Cruise("asd", 100);
        cruise.setMaxSpeed(100);
        cruise.setCapacity(123);
        cruise.setInventor("afoaisf");

        ObjectMapper mapper = new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT);
        mapper.registerModule(new ParameterNamesModule(Mode.PROPERTIES));

        String jsonString = mapper.writeValueAsString( cruise);
        System.out.println(jsonString);

        Cruise anotherCruise = mapper.readValue(jsonString, Cruise.class);
         System.out.println(anotherCruise );
         jsonString = mapper.writeValueAsString( anotherCruise );
         System.out.println(jsonString);

    }

}

다음과 같은 출력이 생성됩니다.

{
  "name" : "asd",
  "capacity" : 123,
  "inventor" : "afoaisf",
  "maxSpeed" : 100
}
Cruise@56f4468b
{
  "name" : "asd",
  "capacity" : 123,
  "inventor" : "afoaisf",
  "maxSpeed" : 100
}

pom 파일에 컴파일러 Args가 있는지 확인합니다.

<compilerArgs>
     <arg>-parameters</arg>
</compilerArgs>

간단한 답변: Java 8 사용,javac -parameters, 및 jackson-syslog-parameter-names

장황한 답변:컨스트럭터가 @JsonCreator로 주석을 달 때 인수에 @JsonProperty로 주석을 달아야 하는 이유는 무엇입니까?

@JsonCreator필요없음에는 필수가 아닙니다.@JsonProperty("xxx")

중첩된 내부 클래스가 있는 경우 정적으로 설정합니다.

public class A{
    private int property1;
    public static class B{
        private String property2;
    }
}

다음 모듈을 등록하면 도움이 됩니다.

JavaTimeModule했습니다.

ObjectMapper mapper = new ObjectMapper();
    mapper.registerModule(new JavaTimeModule());
    mapper.registerModule(new GuavaModule());
    mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE, JsonTypeInfo.As.PROPERTY);
    mapper.configure(MapperFeature.USE_GETTERS_AS_SETTERS, false);
    mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

언급URL : https://stackoverflow.com/questions/39123030/jackson-json-deserialization-with-multiple-parameters-constructor

반응형