7

Using Spring Boot 1.4.4.RELEASE, have saved a RequestBody to MongoDB as below:

{
    "startTime" : NumberLong("1483542955570"),
    "startDate" : ISODate("2017-01-04T15:15:55.570Z"),
    "endTime" : NumberLong("1483542955570"),
    "endDate" : ISODate("2017-01-04T15:15:55.570Z")
}

While mapping this back to a Java POJO, I am trying the below code.

public <T> T getPOJOFromMongoDocument(Document resourceDocument, Class<T> clazz) {
        String serialize = JSON.serialize(resourceDocument);
        return objectMapper.readValue(serialize,
                                      clazz);
}

serialize has the date fields returned as following

"startDate" : { "$date" : "2017-01-04T15:15:55.570Z"}

Due to $date, Jackson ObjectMapper returns the below exception during parsing:

java.lang.RuntimeException: Error parsing mongoDoc to Pojo : errorMessage : {Can not deserialize instance of java.util.Date out of START_OBJECT token at [Source: {
"startTime": 1483542955570,
"startDate": {
    "$date": "2017-01-04T15:15:55.570Z"
},
"endTime": 1483542955570,
"endDate": {
    "$date": "2017-01-04T15:15:55.570Z"
}}; line: 1, column: 381] (through reference chain: com.gofynd.engine.mongo.models.RuleWithDataVO["validity"]->com.gofynd.engine.mongo.models.ValidityVO["startDate"])}

Is there way to solve this without using an ODM?

Manos Nikolaidis
  • 21,608
  • 12
  • 74
  • 82
mukulSharma
  • 113
  • 2
  • 6

2 Answers2

8

When deserializing to Date Jackson expects a String like "2017-01-04T15:15:55.570Z". Instead, it sees the start of another object (the { char) inside the JSON hence the exception.

Consider specifying your Pojo class and another MongoDate class similar to this:

class MongoDate {
    @JsonProperty("$date")
    Date date;
}

class Pojo {
    long startTime;
    long endTime;
    MongoDate startDate;
    MongoDate endDate;
}

Alternatively if you can't / don't want to add a MongoDate class you can introduce a custom deserializer for Date fields. In that case Pojo:

class Pojo {
    long startTime;
    long endTime;
    @JsonDeserialize(using = MongoDateConverter.class)
    Date startDate;
    @JsonDeserialize(using = MongoDateConverter.class)
    Date endDate;
}

And the deserializer would look like this:

class MongoDateConverter extends JsonDeserializer<Date> {
    private static final SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");

    @Override
    public Date deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
        JsonNode node = jp.readValueAsTree();
        try {
            return formatter.parse(node.get("$date").asText());
        } catch (ParseException e) {
            return null;
        }
    }
}
Manos Nikolaidis
  • 21,608
  • 12
  • 74
  • 82
  • But that requires changes to the Java data structure. Wouldn't be a custom Jackson (de)serializer the better solution? – Robert Mar 17 '17 at 16:35
0

I went back and tried a similar approach of using a deserializer.

Here's the code:

public class MongoDateDeserializer extends JsonDeserializer<Date> {

@Override
public Date deserialize(JsonParser jsonParser, DeserializationContext deserializationContext)
        throws IOException {
    ObjectCodec oc = jsonParser.getCodec();
    JsonNode node = oc.readTree(jsonParser);
    String dateValue = node.get("$date")
                           .asText();
    DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
    Date date = null;
    try {
        date = df.parse(dateValue);
    } catch (ParseException e) {
        e.printStackTrace();
    }
    return date;
}}

Changes in VO are are as below:

@JsonDeserialize(using = MongoDateDeserializer.class)
private Date startDate;

@JsonDeserialize(using = MongoDateDeserializer.class)
private Date endDate;

This has worked successfully. However, would be better if MongoDB's JSON.serialize does the job of returning a normalized json. Hopefully in future.

mukulSharma
  • 113
  • 2
  • 6