ModelOptionsUtils: Add ACCEPT_EMPTY_STRING_AS_NULL_OBJECT and ObjectMapper overloads
- Configure OBJECT_MAPPER to accept empty strings as null objects during deserialization. - Add overloaded jsonToMap(String, ObjectMapper) for custom ObjectMapper usage. - Add and update tests to verify correct handling of empty strings for both Map and POJO deserialization, including custom ObjectMapper scenarios. - Clarify documentation and ensure extensibility without global side effects. Fixes #2222 Signed-off-by: Mark Pollack <mark.pollack@broadcom.com>
This commit is contained in:
@@ -69,7 +69,8 @@ public abstract class ModelOptionsUtils {
|
||||
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
|
||||
.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS)
|
||||
.addModules(JacksonUtils.instantiateAvailableModules())
|
||||
.build();
|
||||
.build()
|
||||
.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true);
|
||||
|
||||
private static final List<String> BEAN_MERGE_FIELD_EXCISIONS = List.of("class");
|
||||
|
||||
@@ -82,13 +83,25 @@ public abstract class ModelOptionsUtils {
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts the given JSON string to a Map of String and Object.
|
||||
* Converts the given JSON string to a Map of String and Object using the default
|
||||
* ObjectMapper.
|
||||
* @param json the JSON string to convert to a Map.
|
||||
* @return the converted Map.
|
||||
*/
|
||||
public static Map<String, Object> jsonToMap(String json) {
|
||||
return jsonToMap(json, OBJECT_MAPPER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given JSON string to a Map of String and Object using a custom
|
||||
* ObjectMapper.
|
||||
* @param json the JSON string to convert to a Map.
|
||||
* @param objectMapper the ObjectMapper to use for deserialization.
|
||||
* @return the converted Map.
|
||||
*/
|
||||
public static Map<String, Object> jsonToMap(String json, ObjectMapper objectMapper) {
|
||||
try {
|
||||
return OBJECT_MAPPER.readValue(json, MAP_TYPE_REF);
|
||||
return objectMapper.readValue(json, MAP_TYPE_REF);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
|
||||
@@ -24,6 +24,11 @@ import org.junit.jupiter.api.Test;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.json.JsonMapper;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
|
||||
/**
|
||||
* @author Christian Tzolov
|
||||
*/
|
||||
@@ -122,6 +127,55 @@ public class ModelOptionsUtilsTests {
|
||||
assertThat(target.getSpecificField()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void jsonToMap_emptyStringAsNullObject() {
|
||||
String json = "{\"name\":\"\", \"age\":30}";
|
||||
// For Map: empty string remains ""
|
||||
Map<String, Object> map = ModelOptionsUtils.jsonToMap(json);
|
||||
assertThat(map.get("name")).isEqualTo("");
|
||||
assertThat(map.get("age")).isEqualTo(30);
|
||||
|
||||
// Custom ObjectMapper: still "" for Map
|
||||
ObjectMapper strictMapper = JsonMapper.builder()
|
||||
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
|
||||
.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS)
|
||||
.build()
|
||||
.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, false);
|
||||
Map<String, Object> mapStrict = ModelOptionsUtils.jsonToMap(json, strictMapper);
|
||||
assertThat(mapStrict.get("name")).isEqualTo("");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pojo_emptyStringAsNullObject() throws Exception {
|
||||
String json = "{\"name\":\"\", \"age\":30}";
|
||||
|
||||
// POJO with default OBJECT_MAPPER (feature enabled)
|
||||
Person person = ModelOptionsUtils.OBJECT_MAPPER.readValue(json, Person.class);
|
||||
assertThat(person.name).isEqualTo(""); // String remains ""
|
||||
assertThat(person.age).isEqualTo(30); // Integer is fine
|
||||
|
||||
String jsonWithEmptyAge = "{\"name\":\"John\", \"age\":\"\"}";
|
||||
Person person2 = ModelOptionsUtils.OBJECT_MAPPER.readValue(jsonWithEmptyAge, Person.class);
|
||||
assertThat(person2.name).isEqualTo("John");
|
||||
assertThat(person2.age).isNull(); // Integer: "" → null
|
||||
|
||||
// POJO with feature disabled: should fail for Integer field
|
||||
ObjectMapper strictMapper = JsonMapper.builder()
|
||||
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
|
||||
.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS)
|
||||
.build()
|
||||
.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, false);
|
||||
assertThatThrownBy(() -> strictMapper.readValue(jsonWithEmptyAge, Person.class)).isInstanceOf(Exception.class);
|
||||
}
|
||||
|
||||
public static class Person {
|
||||
|
||||
public String name;
|
||||
|
||||
public Integer age;
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getJsonPropertyValues() {
|
||||
record TestRecord(@JsonProperty("field1") String fieldA, @JsonProperty("field2") String fieldB) {
|
||||
|
||||
Reference in New Issue
Block a user