jackson-databind icon indicating copy to clipboard operation
jackson-databind copied to clipboard

`@JsonValue` ignores propertyInclusion rules for config overide

Open LSwiatek opened this issue 1 month ago • 1 comments

Search before asking

  • [x] I searched in the issues and found nothing similar.

Describe the bug

During serialization of objects using @JsonValue , property inclusion rules are checked from config overide for target type of @JsonValue. For example if global property inclusion is NON_EMPTY, and an overide for string is set to NON_NULL, and a object is using @JsonValue where the value would be an empty string, then output json will not include this property

Version Information

2.16.2, 2.20.0

Reproduction

package com.oberthur.ldz.auxutils.mapper.jackson;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonValue;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

public class JsonValueTest {

    record JsonValuePojo(String stringValue) {
        @JsonValue
        public String toString() {
            return stringValue;
        }
    }

    record WrapperPojo(JsonValuePojo jsonValuePojo) {
    }

    record StringPojo(String stringValue) {
    }

    private static ObjectMapper mapper() {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setDefaultPropertyInclusion(JsonInclude.Include.NON_EMPTY);
        objectMapper.configOverride(String.class)
                .setIncludeAsProperty(JsonInclude.Value.construct(JsonInclude.Include.NON_NULL, JsonInclude.Include.NON_NULL));
        //pretty print
        objectMapper.configure(SerializationFeature.INDENT_OUTPUT, true);
        return objectMapper;
    }

    @Test
    //this test fails
    void jsonValueShouldSerializeToEmptyString() throws JsonProcessingException {
        ObjectMapper objectMapper = mapper();
        String json = objectMapper.writeValueAsString(new WrapperPojo(new JsonValuePojo("")));
        Assertions.assertEquals("""
                {
                  "jsonValuePojo" : ""
                }""", json);
    }

    @Test
    void simplePojoShouldSerializeEmptyString() throws JsonProcessingException {
        ObjectMapper objectMapper = mapper();
        String json = objectMapper.writeValueAsString(new StringPojo(""));
        Assertions.assertEquals("""
                {
                  "stringValue" : ""
                }""", json);

    }
}

Expected behavior

@JsonValue should use the same serialization rules as target type

Additional context

No response

LSwiatek avatar Nov 04 '25 15:11 LSwiatek

@LSwiatek Can you share how the jsonValueShouldSerializeToEmptyString() test fails? With exceptions etc...

So from what I understand, in the failing case the mapper should serialize...

{
     "jsonValuePojo" : {
            "stringValue": ""
      }
}

like this?

May I ask through which configuration you expect the failing case to pass? @LSwiatek

JooHyukKim avatar Nov 05 '25 16:11 JooHyukKim