Kotlin FeatureRequest: Serialize null string to empty string
Is your feature request related to a problem? Please describe. Please give us an option to serialize null strings to empty strings in kotlin.
Describe the solution you'd like
If a kotlin data class has a null string, please convert it to "" in the json.
(We use nullable strings over empty strings, because the null handling is easier, than the empty string handling.
Usage example Some option in the ObjectMapperCustomzier would be nice.
Additional context I tried
@Singleton
class JacksonRegisterCustomObjectMapperCustomizer : ObjectMapperCustomizer {
override fun customize(mapper: ObjectMapper) {
val sp = DefaultSerializerProvider.Impl()
sp.setNullValueSerializer(NullSerializer.instance)
val stringModule = SimpleModule()
stringModule.addSerializer(String::class.javaObjectType, StringSerializer())
stringModule.addSerializer(String::class.javaPrimitiveType, StringSerializer())
stringModule.addSerializer(String::class.java, StringSerializer())
mapper.registerModule(stringModule).setSerializerProvider(sp)
}
class StringSerializer : JsonSerializer<String?>() {
override fun serialize(string: String?, jsonGenerator: JsonGenerator, serializerProvider: SerializerProvider) {
when {
string.isNullOrBlank() -> jsonGenerator.writeString("")
else -> jsonGenerator.writeString(string)
}
}
}
}
but it omits null variables.
I also tried
mapper.configOverride(String.class).setSetterInfo(JsonSetter.Value.forValueNulls(Nulls.AS_EMPTY));
without success.
Are you aware of existence of @JsonSetter functionality for null-handling (via annotations and/or per-type "config overrides")? They are briefly explained wrt Jackson 2.9 features:
https://cowtowncoder.medium.com/jackson-2-9-features-b2a19029e9ff
specifically, for Strings, you could:
// Prevent assigned of `null` for any `String` property!
mapper.configOverride(String.class)
.setSetterInfo(JsonSetter.Value.forValueNulls(Nulls.FAIL));
or, in your case, use Nulls.AS_EMPTY to get "" assigned.
@cowtowncoder Thank you for your response. Yes, I tried that without success. Maybe because I use Kotlin.
Ahhhh. My mistake; this feature only works for deserialization, not serialization.
One request: could you change the title to mention "serialization" (not "deserialization") -- easier to avoid mistakes like mine.
@cowtowncoder Argh, I'm sorry, I mixed it by myself as well. English is not my native tongue. xD
Np at all, thanks!
@cowtowncoder Just to confirm, I'm guessing because you transferred this that customizing serialization of nulls is something that should happen in this module, not in jackson-databind?
@dinomite it's bit of a tricky case: it might make sense on either side, depending. But at this point request itself was for Kotlin handling which is why I moved it -- does not mean it could not result in another issue on jackson-databind side. But at this point I think the problem is that I would not want a String-specific handling on databind at least; there should be something more widely applicable. But I don't really have a good suggestion or plan for more general translation on output... come to think of this I am not even sure I think it belongs in Jackson (wrt "Validation and Transformation are out of scope"). I am not sure it does not, either. :)
So. I think this particular request makes sense here, but likely work, if any, would need to think carefully about what to do, if anything, and where.
Not sure if this helps but that's my current thinking.
Makes sense, thanks for confirming.
I have created a simple sample that accomplishes this. Since it is feasible with existing functionality and has not received any votes, this issue is closed.
import com.fasterxml.jackson.core.JsonGenerator
import com.fasterxml.jackson.databind.JsonSerializer
import com.fasterxml.jackson.databind.SerializerProvider
import com.fasterxml.jackson.databind.introspect.Annotated
import com.fasterxml.jackson.databind.introspect.NopAnnotationIntrospector
import org.junit.Test
import kotlin.test.assertEquals
object NullStrToEmptyStrSerializer : JsonSerializer<String?>() {
override fun serialize(string: String?, jsonGenerator: JsonGenerator, serializerProvider: SerializerProvider) {
when {
string.isNullOrBlank() -> jsonGenerator.writeString("")
else -> jsonGenerator.writeString(string)
}
}
}
object NullStrToEmptyStrAnnotationIntrospector : NopAnnotationIntrospector() {
override fun findNullSerializer(am: Annotated): Any? = am.takeIf { it.type.rawClass == String::class.java }
?.let { NullStrToEmptyStrSerializer }
}
data class D(val s: String?, val i: Int?)
class NullSerTest {
@Test
fun test() {
val mapper = jacksonObjectMapper().setAnnotationIntrospector(NullStrToEmptyStrAnnotationIntrospector)
assertEquals("""{"s":"","i":null}""", mapper.writeValueAsString(D(null, null)))
assertEquals("""{"s":"aaa","i":1}""", mapper.writeValueAsString(D("aaa", 1)))
}
}