Jackson과 매핑 할 때 기본값을 null 필드로 설정


80

Jackson을 사용하여 일부 JSON 개체를 Java 개체에 매핑하려고합니다. JSON 객체의 일부 필드는 필수이며 (로 표시 할 수 있음 @NotNull) 일부는 선택 사항입니다.

Jackson과의 매핑 후 JSON 개체에 설정되지 않은 모든 필드는 Java에서 null 값을 갖습니다. @NotNullnull 인 경우 Jackson에게 Java 클래스 멤버에 기본값을 설정하도록 지시 할 수 있는 유사한 주석 이 있습니까?

편집 : 질문을 더 명확하게하기 위해 몇 가지 코드 예제가 있습니다.

Java 객체 :

class JavaObject {
    @NotNull
    public String notNullMember;

    @DefaultValue("Value")
    public String optionalMember;
}

JSON 객체는 다음 중 하나 일 수 있습니다.

{
    "notNullMember" : "notNull"
}

또는:

{
    "notNullMember" : "notNull",
    "optionalMember" : "optional"
}

@DefaultValue주석 그냥 내가 요구하고 무엇을 보여주는 것입니다. 실제 주석이 아닙니다. json으로 개체가 첫 번째 예에서와 같이 있으면 난의 값이 원하는 optionalMember일을 "Value"하지 null. 그런 일을하는 주석이 있습니까?


비슷한 질문을 찾고 있지만 최종 필드입니다.
Daneel Yaitskov

답변:


82

기본값을 설정하는 주석이 없습니다.
Java 클래스 수준에서만 기본값을 설정할 수 있습니다.

public class JavaObject 
{
    public String notNullMember;

    public String optionalMember = "Value";
}

56
optionalMember에 대한 json이없는 경우 기본값을 제공합니다. "널 (null)"로 설정이 JSON 인 경우, 널 (null)이 아닌 "값"입니다
리 Meador


4
또한 클래스에 AllArgsConstructor가있는 경우 기본값은 null로 지워집니다. stackoverflow.com/q/48725922/9640261
NayoR

@LeeMeador,이 경우 값이 "null"이기 때문일까요?
pellyadolfo 19

32

제안 된 솔루션 중 하나만 명시 적으로 설정된 default-value시기를 유지합니다 some-value:null(POJO 가독성이 손실되고 어색함).

유지 default-value하고 절대 설정하지 않는 방법은 다음과 같습니다.null

@JsonProperty("some-value")
public String someValue = "default-value";

@JsonSetter("some-value")
public void setSomeValue(String s) {
    if (s != null) { 
        someValue = s; 
    }
}

1
수업의 모든 분야에서 할 수있는 방법이 있을까요?
Woland

5

고유 한 JsonDeserializer를 만들고 해당 속성에 주석을 달 수 있습니다. @JsonDeserialize(as = DefaultZero.class)

예 : BigDecimal을 기본값으로 0으로 구성하려면 다음을 수행하십시오.

public static class DefaultZero extends JsonDeserializer<BigDecimal> {
    private final JsonDeserializer<BigDecimal> delegate;

    public DefaultZero(JsonDeserializer<BigDecimal> delegate) {
        this.delegate = delegate;
    }

    @Override
    public BigDecimal deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
        return jsonParser.getDecimalValue();
    }

    @Override
    public BigDecimal getNullValue(DeserializationContext ctxt) throws JsonMappingException {
        return BigDecimal.ZERO;
    }
}

그리고 사용법 :

class Sth {

   @JsonDeserialize(as = DefaultZero.class)
   BigDecimal property;
 }

4

해결책은 기본 생성자 내부의 속성 값을 설정하는 것 같습니다. 따라서이 경우 Java 클래스는 다음과 같습니다.

class JavaObject {

    public JavaObject() {

        optionalMember = "Value";
    }

    @NotNull
    public String notNullMember;

    public String optionalMember;
}

Jackson과의 매핑 후 optionalMemberJSON에서이 누락 된 경우 Java 클래스의 값은 "Value".

그러나 주석이 있고 기본 생성자가없는 솔루션이 있는지 여전히 알고 싶습니다.


6
그러나 JSON에 null 값이 있으면 optionalMember는 null로 끝납니다. 그것이 원할 수도 있고 아닐 수도 있습니다.
Lee Meador

2
이것은 필드에 기본값을 설정하는 것과 동일합니다. 문제를 해결하지 못합니다.
Innokenty

2

구성원을 비공개로 만들고 setter / getter 쌍을 추가합니다. setter에서 null이면 대신 기본값을 설정하십시오. 또한 내부 값이 null 일 때 기본값을 반환하는 getter를 사용하여 스 니펫을 표시했습니다.

class JavaObject {
    private static final String DEFAULT="Default Value";

    public JavaObject() {
    }

    @NotNull
    private String notNullMember;
    public void setNotNullMember(String value){
            if (value==null) { notNullMember=DEFAULT; return; }
            notNullMember=value;
            return;
    }

    public String getNotNullMember(){
            if (notNullMember==null) { return DEFAULT;}
            return notNullMember;
    }

    public String optionalMember;
}

1
Setter는 null이 아닌 값을 설정하므로 notNullMember가 설정되지 않은 경우에만 호출 될 때마다 getter 값이 if 문에 들어갑니다.
NayoR

0

비슷한 문제가 있었지만 제 경우에는 기본값이 데이터베이스에있었습니다. 다음은 그에 대한 해결책입니다.

 @Configuration
 public class AppConfiguration {
 @Autowired
 private AppConfigDao appConfigDao;

 @Bean
 public Jackson2ObjectMapperBuilder builder() {
   Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder()
       .deserializerByType(SomeDto.class, 
 new SomeDtoJsonDeserializer(appConfigDao.findDefaultValue()));
   return builder;
 }

그런 다음 필드 / 객체가 null 인 경우 json을 역 직렬화하고 기본값을 설정 SomeDtoJsonDeserializer하는 ObjectMapper데 사용 됩니다.


0

이미 많은 좋은 제안이 있지만 여기에 하나 더 있습니다. @JsonDeserialize를 사용하여 Jackson이 post-deserialization을 호출하는 임의의 "sanitizer"를 수행 할 수 있습니다.

@JsonDeserialize(converter=Message1._Sanitizer.class)  
public class Message1 extends MessageBase
{
    public String string1 = "";
    public int integer1;

    public static class _Sanitizer extends StdConverter<Message1,Message1> {
        @Override
        public Message1 convert(Message1 message) {
            if (message.string1 == null) message.string1 = "";
            return message;
        }
    }
}

0

또 다른 옵션은 InjectableValues@JacksonInject 를 사용하는 입니다. 항상 동일한 값이 아니라 DB 또는 특정 경우 다른 곳에서 가져 오는 값을 사용해야하는 경우 매우 유용합니다. 다음은 사용 예입니다 JacksonInject.

protected static class Some {
    private final String field1;
    private final String field2;

    public Some(@JsonProperty("field1") final String field1,
            @JsonProperty("field2") @JacksonInject(value = "defaultValueForField2",
                    useInput = OptBoolean.TRUE) final String field2) {
        this.field1 = requireNonNull(field1);
        this.field2 = requireNonNull(field2);
    }

    public String getField1() {
        return field1;
    }

    public String getField2() {
        return field2;
    }
}

@Test
public void testReadValueInjectables() throws JsonParseException, JsonMappingException, IOException {
    final ObjectMapper mapper = new ObjectMapper();
    final InjectableValues injectableValues =
            new InjectableValues.Std().addValue("defaultValueForField2", "somedefaultValue");
    mapper.setInjectableValues(injectableValues);

    final Some actualValueMissing = mapper.readValue("{\"field1\": \"field1value\"}", Some.class);
    assertEquals(actualValueMissing.getField1(), "field1value");
    assertEquals(actualValueMissing.getField2(), "somedefaultValue");

    final Some actualValuePresent =
            mapper.readValue("{\"field1\": \"field1value\", \"field2\": \"field2value\"}", Some.class);
    assertEquals(actualValuePresent.getField1(), "field1value");
    assertEquals(actualValuePresent.getField2(), "field2value");
}

생성자를 사용하여 엔티티를 생성하는 경우 (일반적으로 lombok 에서 @Value 또는 @AllArgsConstructor 를 사용할 때 발생 함 ) 생성자가 아니라 속성에 넣는 경우 예상대로 작동하지 않습니다-주입 된 값 필드는 항상 상관없이 넣어 여부, JSON으로 값을 대체하지 않습니다 에 . 이는 jackson이 생성자가 호출 된 후 해당 속성을 주입하기 때문입니다 (속성이 인 경우에도 )-필드가 생성자에서 올바른 값으로 설정되었지만 재정의되었습니다 (확인 : https://github.com/FasterXML/jackson-databind/ 문제 / 2678 (@JacksonInjectuseInput = OptBoolean.TRUE@JacksonInjectfinal https://github.com/rzwitserloot/lombok/issues/1528#issuecomment-607725333자세한 내용은이 테스트를 통과했습니다 .

protected static class Some {
    private final String field1;

    @JacksonInject(value = "defaultValueForField2", useInput = OptBoolean.TRUE)
    private final String field2;

    public Some(@JsonProperty("field1") final String field1,
            @JsonProperty("field2") @JacksonInject(value = "defaultValueForField2",
                    useInput = OptBoolean.TRUE) final String field2) {
        this.field1 = requireNonNull(field1);
        this.field2 = requireNonNull(field2);
    }

    public String getField1() {
        return field1;
    }

    public String getField2() {
        return field2;
    }
}

@Test
public void testReadValueInjectablesIncorrectBehavior() throws JsonParseException, JsonMappingException, IOException {
    final ObjectMapper mapper = new ObjectMapper();
    final InjectableValues injectableValues =
            new InjectableValues.Std().addValue("defaultValueForField2", "somedefaultValue");
    mapper.setInjectableValues(injectableValues);

    final Some actualValueMissing = mapper.readValue("{\"field1\": \"field1value\"}", Some.class);
    assertEquals(actualValueMissing.getField1(), "field1value");
    assertEquals(actualValueMissing.getField2(), "somedefaultValue");

    final Some actualValuePresent =
            mapper.readValue("{\"field1\": \"field1value\", \"field2\": \"field2value\"}", Some.class);
    assertEquals(actualValuePresent.getField1(), "field1value");
    // unfortunately "field2value" is overrided because of putting "@JacksonInject" to the field
    assertEquals(actualValuePresent.getField2(), "somedefaultValue");
}

이것이 비슷한 문제를 가진 사람에게 도움이되기를 바랍니다.

PS 저는 jackson v. 2.9.6을 사용하고 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.