Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to deserialize Jackson Json NULL String to Date with JsonFormat

I have looked a lot but still couldn't get the answer so far, any help is really appreciated!

I have a simple String to Date field mapping and try to read a JSON string to Java object.

@JsonInclude(value=Include.NON_EMPTY)
@JsonFormat(shape=JsonFormat.Shape.STRING, pattern="dd-MMM-yyyy", timezone="PST")
protected Date eolAnnounceDate;

However I am getting the following exception if the JSON string value is empty. Can you someone tell me how to get around this? I have tried a few options but they are all for serialization.

ObjectMapper objectMapper = new ObjectMapper();   
objectMapper.setSerializationInclusion(Include.NON_NULL); 
objectMapper.setSerializationInclusion(Include.NON_EMPTY);

Exception :

java.lang.IllegalArgumentException: Failed to parse Date value 'NULL' (format: "dd-MMM-yyyy"): Unparseable date: "NULL" com.fasterxml.jackson.databind.deser.std.DateDeserializers$DateBasedDeserializer._parseDate(DateDeserializers.java:180) com.fasterxml.jackson.databind.deser.std.DateDeserializers$DateDeserializer.deserialize(DateDeserializers.java:279) com.fasterxml.jackson.databind.deser.std.DateDeserializers$DateDeserializer.deserialize(DateDeserializers.java:260) com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:464) com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:98) com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:295) com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:121) com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:230) com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:207) com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:23) com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:464) com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:98) com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:295) com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:121) com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:2888) com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2034) com.cisco.cre.dao.impl.ElasticsearchDAOImpl.getListByIdsFilter(ElasticsearchDAOImpl.java:94)

Thanks - Atul

like image 809
apald Avatar asked Dec 01 '22 18:12

apald


1 Answers

Your problem is not that a null value is passed in the JSON. The problem is that the JSON contains a string that has the value "NULL".

So, in order to fix this there are a number of available approaches. I think that the following two will work for this case.

Alternative 1: Fix the JSON

The first alternative is to fix the JSON so that it does not contain the the string value "NULL" and instead contain the value null (not quoted) or simply skip it.

Imagine the following POJO:

public class DatePojo {
    @JsonInclude(value= JsonInclude.Include.NON_EMPTY)
    @JsonFormat(shape=JsonFormat.Shape.STRING, pattern="dd-MMM-yyyy", timezone="PST")
    @JsonProperty("date")
    private Date date;
}

The following test shows that valid dates, empty values and null values work:

@Test
public void testJson() throws IOException {
    String jsonWithValidDate = "{\"date\":\"12-Jun-1982\"}";
    String jsonWithNoDate = "{}";
    String jsonWithNullDate = "{\"date\":null}";

    ObjectMapper mapper = new ObjectMapper();
    final DatePojo pojoWithValidDate = mapper.readValue(jsonWithValidDate, DatePojo.class);
    final DatePojo pojoWithNoDate = mapper.readValue(jsonWithNoDate, DatePojo.class);
    final DatePojo pojoWithNullDate = mapper.readValue(jsonWithNullDate, DatePojo.class);

    Assert.assertNotNull(pojoWithValidDate.date);
    Assert.assertNull(pojoWithNoDate.date);
    Assert.assertNull(pojoWithNullDate.date);
}

However, if you pass along the value "NULL" the test fails since "NULL" can not be parsed as a date:

@Test(expected = JsonMappingException.class)
public void testInvalidJson() throws IOException {
    String jsonWithNullString = "{\"date\":\"NULL\"}";

    ObjectMapper mapper = new ObjectMapper();
    mapper.readValue(jsonWithNullString, DatePojo.class); // Throws the exception
    Assert.fail();
}

Alternative 2: provide your own converter that handles "NULL"

If it is not possible to fix the JSON (as described in alternative 1) you can provide your own converter.

Setup your pojo like this instead:

public class DatePojo {
    @JsonProperty("date")
    @JsonDeserialize(converter = MyDateConverter.class)
    private Date date;
}

And provide a converter along the lines of:

public class MyDateConverter extends StdConverter<String, Date> {
    @Override
    public Date convert(final String value) {
        if (value == null || value.equals("NULL")) {
            return null;
        }
        try {
            return new SimpleDateFormat("dd-MMM-yyyy").parse(value);
        } catch (ParseException e) {
            throw new IllegalStateException("Unable to parse date", e);
        }
    }
}

Then, you should be all set. The following test passes:

@Test
public void testJson() throws IOException {
    String jsonWithValidDate = "{\"date\":\"12-Jun-1982\"}";
    String jsonWithNoDate = "{}";
    String jsonWithNullDate = "{\"date\":null}";
    String jsonWithNullString = "{\"date\":\"NULL\"}"; // "NULL"

    ObjectMapper mapper = new ObjectMapper();
    final DatePojo pojoWithValidDate = mapper.readValue(jsonWithValidDate, DatePojo.class);
    final DatePojo pojoWithNoDate = mapper.readValue(jsonWithNoDate, DatePojo.class);
    final DatePojo pojoWithNullDate = mapper.readValue(jsonWithNullDate, DatePojo.class);
    final DatePojo pojoWithNullStr = mapper.readValue(jsonWithNullString, DatePojo.class); // Works

    Assert.assertNotNull(pojoWithValidDate.date);
    Assert.assertNull(pojoWithNoDate.date);
    Assert.assertNull(pojoWithNullDate.date);
    Assert.assertNull(pojoWithNullStr.date); // Works
}

IMO, the best approach is to use alternative 1 where you simply change the JSON.

like image 56
wassgren Avatar answered Dec 04 '22 05:12

wassgren