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;
}
Answer from Sachin Gupta on Stack OverflowJackson 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.
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("");
}
}
My previous answer was unacceptable due to it not including annotations and serializers. So referring to this link (which I found on google by searching "jackson 2.0 custom serializer"), I imagine this should work (assuming your class is Person since it's not specified):
public class PersonSerializer extends JsonSerializer<Person> {
@Override
public void serialize(Person value, JsonGenerator jgen, SerializerProvider provider)
throws IOException, JsonProcessingException {
jgen.writeStartObject();
jgen.writeStringField("name", value.name);
if (value.first == null) {
jgen.writeStringField("first", "");
}else{
jgen.writeStringField("first", value.first);
}
if(value.last == null){
jgen.writeStringField("last", "");
}else{
jgen.writeStringField("last", value.last);
}
jgen.writeEndObject();
}
}
And then on your person class
@JsonSerialize(using = PersonSerializer.class)
public class Person {
//First/Last name etc
}
I think you should re-evaluate why you want a custom parser for this, if you continue reading the whole link at the top of this answer you'll see the use case for custom parsers and how it's different than yours. In his case he wants to return some data that's not directly related to that field, whereas you just wanted to have null treated as an empty string.
You can override the setFirst and setLast.
public void setFirst(String first) {
if(first == null) {
this.first = "";
} else {
this.first = first;
}
}
In a JSON "object" (aka dictionary), there are two ways to represent absent values: Either have no key/value pair at all, or have a key with the JSON value null.
So you either use .add with a proper value what will get translated to null when you build the JSON, or you don't have the .add call.
It is a JSON-B design deficiency. They could have done something slick like:
Json.createObjectBuilder().addIfNotNull("address", this.getAddress());
Json.createObjectBuilder().add("address", this.getAddress(), defaultOnNull);
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);
Try to set JSONObject.NULL instead of null:
A sentinel value used to explicitly define a name with no value. Unlike null, names with this value:
- show up in the names() array
- show up in the keys() iterator
- return true for has(String)
- do not throw on get(String)
- are included in the encoded JSON string.
This value violates the general contract of equals(Object) by returning true when compared to null. Its toString() method returns "null".
For me with net.sf.json.JSONObject I need to create a null JSON by
new JSONObject(true)
This is how I get class into groovy:
groovy.grape.Grape.grab(group: "org.kohsuke.stapler", module: "json-lib", version: "2.4-jenkins-2")
The concatenation in your string is not alright. Is abc an object? or is it just the string abc?
String test = "{" + "abc" + ":null}";
but in the above case, you should simply do:
String test = "{abc:null}";
Or if abc is another defined String variable, say like this:
String abc = "awesomeText";
String test = "{" + abc + ":null}";
Or maybe you need those quotes from abc? use "\" to escape the quote character. Like this:
String test ="{\"abc\":null}";
I made this test and it worked fine for me:
public static void main(String[] args) {
BufferedReader br;
try {
br = new BufferedReader(new FileReader("C:\\test.json"));
String currentLine;
while ((currentLine = br.readLine()) != null) {
JSONObject testObj = new JSONObject(currentLine);
System.out.println(testObj);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
}
I tried many successful tests while playing with the content of the test.json file:
{"abc":null}{'abc':null}{abc:null}
Since someone asked this question again a few minutes ago, I did some research and think I found a good solution to this problem (when dealing with Strings).
You have to create a custom JsonDeserializer class as follows:
class NullStringJsonDeserializer extends JsonDeserializer<String> {
@Override
public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
String result = StringDeserializer.instance.deserialize(p, ctxt);
return result!=null && result.toLowerCase().equals(null+"") ? null : result;
}
}
Last but not least, all you have to do is tell your ObjectMapper that it should use your custom json string deserializer. This depends on how and where you use your ObjectMapper but could look like that:
ObjectMapper objectMapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addDeserializer(String.class, new NullStringJsonDeserializer());
objectMapper.registerModule(module);
If you use a custom Deserializer, you can do this, but I don't think it's available using standard annotations.
Borrowing and modifying some code from http://www.baeldung.com/jackson-deserialization :
public class ItemDeserializer extends JsonDeserializer<Item> {
@Override
public Item deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
JsonNode node = jp.getCodec().readTree(jp);
String title = null;
TextNode titleNode = (TextNode)node.get("title");
if ( ! titleNode.toString().equals("null")) {
title = titleNode.toString();
}
Date expires = null;
// similar logic for expires
return new Item(title, expires);
}
}
Let's evaluate the parsing of each:
http://jsfiddle.net/brandonscript/Y2dGv/
var json1 = '{}';
var json2 = '{"myCount": null}';
var json3 = '{"myCount": 0}';
var json4 = '{"myString": ""}';
var json5 = '{"myString": "null"}';
var json6 = '{"myArray": []}';
console.log(JSON.parse(json1)); // {}
console.log(JSON.parse(json2)); // {myCount: null}
console.log(JSON.parse(json3)); // {myCount: 0}
console.log(JSON.parse(json4)); // {myString: ""}
console.log(JSON.parse(json5)); // {myString: "null"}
console.log(JSON.parse(json6)); // {myArray: []}
The tl;dr here:
The fragment in the json2 variable is the way the JSON spec indicates
nullshould be represented. But as always, it depends on what you're doing -- sometimes the "right" way to do it doesn't always work for your situation. Use your judgement and make an informed decision.
JSON1 {}
This returns an empty object. There is no data there, and it's only going to tell you that whatever key you're looking for (be it myCount or something else) is of type undefined.
JSON2 {"myCount": null}
In this case, myCount is actually defined, albeit its value is null. This is not the same as both "not undefined and not null", and if you were testing for one condition or the other, this might succeed whereas JSON1 would fail.
This is the definitive way to represent null per the JSON spec.
JSON3 {"myCount": 0}
In this case, myCount is 0. That's not the same as null, and it's not the same as false. If your conditional statement evaluates myCount > 0, then this might be worthwhile to have. Moreover, if you're running calculations based on the value here, 0 could be useful. If you're trying to test for null however, this is actually not going to work at all.
JSON4 {"myString": ""}
In this case, you're getting an empty string. Again, as with JSON2, it's defined, but it's empty. You could test for if (obj.myString == "") but you could not test for null or undefined.
JSON5 {"myString": "null"}
This is probably going to get you in trouble, because you're setting the string value to null; in this case, obj.myString == "null" however it is not == null.
JSON6 {"myArray": []}
This will tell you that your array myArray exists, but it's empty. This is useful if you're trying to perform a count or evaluation on myArray. For instance, say you wanted to evaluate the number of photos a user posted - you could do myArray.length and it would return 0: defined, but no photos posted.
null is not zero. It is not a value, per se: it is a value outside the domain of the variable indicating missing or unknown data.
There is only one way to represent null in JSON. Per the specs (RFC 4627 and json.org):
2.1. Values A JSON value MUST be an object, array, number, or string, or one of the following three literal names: false null true

Honestly, I really wouldn't spend time on making a payload "look nice". Now if you said that you were motivated to keep the payload small for efficiency reasons I'd buy that.
Perhaps you use Jackson for serialising to JSON as well (I don't see why you are using two different libraries). I think that this question shows that Jackson's treatment of nulls can be controlled.
Change your get method. Set if(something!=null)return something; instead of return something;