Add NullKeyToEmptyStringSerializer - to allow jackson serialization of a map with a null key

This commit is contained in:
2024-12-11 14:40:06 -06:00
parent f3546da8cc
commit 5434721c8e
2 changed files with 74 additions and 1 deletions

View File

@ -30,11 +30,14 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.Consumer; import java.util.function.Consumer;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter; import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.kingsrook.qqq.backend.core.logging.QLogger; import com.kingsrook.qqq.backend.core.logging.QLogger;
import com.kingsrook.qqq.backend.core.model.data.QRecord; import com.kingsrook.qqq.backend.core.model.data.QRecord;
@ -54,6 +57,11 @@ public class JsonUtils
{ {
private static final QLogger LOG = QLogger.getLogger(JsonUtils.class); private static final QLogger LOG = QLogger.getLogger(JsonUtils.class);
//////////////////////////////////////////////////////////////////////
// see https://www.baeldung.com/jackson-map-null-values-or-null-key //
//////////////////////////////////////////////////////////////////////
public static NullKeyToEmptyStringSerializer nullKeyToEmptyStringSerializer = new NullKeyToEmptyStringSerializer();
/******************************************************************************* /*******************************************************************************
@ -396,4 +404,41 @@ public class JsonUtils
return (record); return (record);
} }
/***************************************************************************
**
***************************************************************************/
public static class NullKeyToEmptyStringSerializer extends StdSerializer<Object>
{
/***************************************************************************
**
***************************************************************************/
public NullKeyToEmptyStringSerializer()
{
this(null);
}
/***************************************************************************
**
***************************************************************************/
public NullKeyToEmptyStringSerializer(Class<Object> t)
{
super(t);
}
/***************************************************************************
**
***************************************************************************/
@Override
public void serialize(Object nullKey, JsonGenerator jsonGenerator, SerializerProvider unused) throws IOException
{
jsonGenerator.writeFieldName("");
}
}
} }

View File

@ -35,11 +35,13 @@ import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterOrderBy; import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterOrderBy;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter; import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
import com.kingsrook.qqq.backend.core.model.data.QRecord; import com.kingsrook.qqq.backend.core.model.data.QRecord;
import com.kingsrook.qqq.backend.core.utils.collections.MapBuilder;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotNull;
@ -318,4 +320,30 @@ class JsonUtilsTest extends BaseTest
assertEquals("age", qQueryFilter.getOrderBys().get(0).getFieldName()); assertEquals("age", qQueryFilter.getOrderBys().get(0).getFieldName());
assertTrue(qQueryFilter.getOrderBys().get(0).getIsAscending()); assertTrue(qQueryFilter.getOrderBys().get(0).getIsAscending());
} }
/*******************************************************************************
**
*******************************************************************************/
@Test
void testNullKeyInMap()
{
Map<Object, String> mapWithNullKey = MapBuilder.of(null, "foo");
//////////////////////////////////////////////////////
// assert default behavior throws with null map key //
//////////////////////////////////////////////////////
assertThatThrownBy(() -> JsonUtils.toJson(mapWithNullKey)).rootCause().hasMessageContaining("Null key for a Map not allowed in JSON");
////////////////////////////////////////////////////////////////////////
// assert that the nullKeyToEmptyStringSerializer does what we expect //
////////////////////////////////////////////////////////////////////////
assertEquals("""
{"":"foo"}""", JsonUtils.toJson(mapWithNullKey, mapper ->
{
mapper.getSerializerProvider().setNullKeySerializer(JsonUtils.nullKeyToEmptyStringSerializer);
}));
}
} }