See the docs on Custom Serializers; there's an example of exactly this, works for me.
In case the docs move let me paste the relevant answer:
Answer from enigment on Stack OverflowConverting null values to something else
(like empty Strings)
If you want to output some other JSON value instead of null (mainly because some other processing tools prefer other constant values -- often empty String), things are bit trickier as nominal type may be anything; and while you could register serializer for
Object.class, it would not be used unless there wasn't more specific serializer to use.But there is specific concept of "null serializer" that you can use as follows:
// Configuration of ObjectMapper: { // First: need a custom serializer provider StdSerializerProvider sp = new StdSerializerProvider(); sp.setNullValueSerializer(new NullSerializer()); // And then configure mapper to use it ObjectMapper m = new ObjectMapper(); m.setSerializerProvider(sp); } // serialization as done using regular ObjectMapper.writeValue() // and NullSerializer can be something as simple as: public class NullSerializer extends JsonSerializer<Object> { public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException { // any JSON value you want... jgen.writeString(""); } }
java - Change JSON value from null to empty String - Stack Overflow
json - gson: Treat null as empty String - Stack Overflow
How to deserialize a blank JSON string value to null for java.lang.String? - Stack Overflow
java - Spring boot, Jackson Convert empty string into NULL in Serialization - Stack Overflow
There are a couple of ways to achieve custom null value serialising:
If you want to serialise a null as an empty String, try using this annotation on a property, or setter:
@JsonSetter(nulls=Nulls.AS_EMPTY)
or the same for specific mapper:
MAPPER.configOverride(String.class).setSetterInfo(JsonSetter.Value.forValueNulls(Nulls.AS_EMPTY));
- You can initialise properties with default values on the declaration or in the getter.
- As you've already mentioned, by providing a custom serialiser.
I did try your code, and that serialised null value as expected when using an ObjectMapper instead of JodaMapper. Is there any particular reason for using a JodaMapper?
I found the solution.... I had
@JsonInclude(JsonInclude.Include.NON_NULL)
at class level in the class that I wanted to serialize. When I remove the annotation I the code above works.
You can override default ObjectMapper (provided by Spring Boot auto-configuration) and configure globally format to use for properties of type String.
@Configuration
public class JacksonConfiguration {
@Bean
ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
final var objectMapper = builder.createXmlMapper(false).build();
objectMapper.configOverride(String.class).setSetterInfo(JsonSetter.Value.forValueNulls(Nulls.AS_EMPTY));
return objectMapper;
}
}
DefaultSerializerProvider.Impl sp = new DefaultSerializerProvider.Impl();
sp.setNullValueSerializer(new NullSerializer());
new ObjectMapper().setSerializerProvider(sp)...
Where
public class NullSerializer extends JsonSerializer<Object> {
@Override
public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeString("");
}
}
The above answer works okay for serialisation, but on deserialisation, if there is a field with null value, Gson will skip it and won't enter the deserialize method of the type adapter, therefore you need to register a TypeAdapterFactory and return type adapter in it.
Gson gson = GsonBuilder().registerTypeAdapterFactory(new NullStringToEmptyAdapterFactory()).create();
public static class NullStringToEmptyAdapterFactory<T> implements TypeAdapterFactory {
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
Class<T> rawType = (Class<T>) type.getRawType();
if (rawType != String.class) {
return null;
}
return (TypeAdapter<T>) new StringAdapter();
}
}
public static class StringAdapter extends TypeAdapter<String> {
public String read(JsonReader reader) throws IOException {
if (reader.peek() == JsonToken.NULL) {
reader.nextNull();
return "";
}
return reader.nextString();
}
public void write(JsonWriter writer, String value) throws IOException {
if (value == null) {
writer.nullValue();
return;
}
writer.value(value);
}
}
In the actual version of gson you can do that:
Object instance = c.getConstructor().newInstance();
GsonBuilder gb = new GsonBuilder();
gb.serializeNulls();
Gson gson = gb.create();
String stringJson = gson.toJson(instance);
Jackson will give you null for other objects, but for String it will give empty String.
But you can use a Custom JsonDeserializer to do this:
class CustomDeserializer extends JsonDeserializer<String> {
@Override
public String deserialize(JsonParser jsonParser, DeserializationContext context) throws IOException, JsonProcessingException {
JsonNode node = jsonParser.readValueAsTree();
if (node.asText().isBlank()) {
return null;
}
return node.toString();
}
}
In class you have to use it for location field:
class EventBean {
public Long eventId;
public String title;
@JsonDeserialize(using = CustomDeserializer.class)
public String location;
}
It is possible to define a custom deserializer for the String type, overriding the standard String deserializer:
this.mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addDeserializer(String.class, new StdDeserializer<String>(String.class) {
@Override
public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
String result = StringDeserializer.instance.deserialize(p, ctxt);
if (StringUtils.isEmpty(result)) {
return null;
}
return result;
}
});
mapper.registerModule(module);
This way all String fields will behave the same way.
Using:
-
.net 8
-
System.Text.Json serializer.
Let's say I have a two classes defined like this:
public class SampleClass
{
public SampleClassDetail? FirstProperty { get; set; }
public SampleClassDetail? SecondProperty { get; set; }
}
public class SampleClassDetail
{
public int MyProperty { get; set; }
}And an instance of `SampleClass` :
SampleClass sample = new()
{
FirstProperty = new SampleClassDetail { MyProperty = 1 }
};Then I want to serialize it with `System.Text.Json`:
string serialized = JsonSerializer.Serialize(sample);
This produces:
{
"FirstProperty": {
"MyProperty": 1
},
"SecondProperty": null
}But I would the null to be treated like: `"SecondProperty": {}`
I've tried creating a CustomJson Converter and adding it to the options:
public class SampleClassDetailConverter : JsonConverter<SampleClassDetail?>
{
public override SampleClassDetail? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
throw new NotImplementedException();
}
public override void Write(Utf8JsonWriter writer, SampleClassDetail? value, JsonSerializerOptions options)
{
if (value == null)
{
writer.WriteStartObject();
writer.WriteEndObject();
}
else
{
var newOptions = new JsonSerializerOptions(options);
newOptions.Converters.Remove(this);
JsonSerializer.Serialize(writer, value, newOptions);
}
}
}
var options = new JsonSerializerOptions();
options.Converters.Add(new SampleClassDetailConverter());
string serialized = JsonSerializer.Serialize(sample, options);But still getting the same.
I've created a dotnetfiddle:
https://dotnetfiddle.net/PsXrxt
Any idea how to achieve this?