Jackson and empty string - java

I have the following code:
import javax.xml.datatype.XMLGregorianCalendar;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
public class Test {
public static void main(String[] args) {
print("");//Prints null
print("2013-06-14T01:23:47.547+0000"); //Prints the date
print("&&&&AD");//Throws error
}
private static void print(String dateString) {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false );
try {
String x = "{\"dateTime\": \""+dateString+"\"}";
Foo type = mapper.readValue(x, Foo.class);
System.out.println("Datetime is " + type.getDateTime());
} catch (Exception e) {
e.printStackTrace();
}
}
private static class Foo {
private XMLGregorianCalendar dateTime;
public XMLGregorianCalendar getDateTime() {
return dateTime;
}
public void setDateTime(XMLGregorianCalendar dateTime) {
this.dateTime = dateTime;
}
}
}
When the String value is blank "", then Jackson treats the value as null, but when I put some invalid value such as "&&&&AD", it tries to convert it to XML date Time and throws error.
The error I get is:
com.fasterxml.jackson.databind.exc.InvalidFormatException: Can not construct instance of javax.xml.datatype.XMLGregorianCalendar from String value '&&&&AD': not a valid representation (error: Failed to parse Date value '&&&&AD': Can not parse date "&&&&AD": not compatible with any of standard forms ("yyyy-MM-dd'T'HH:mm:ss.SSSZ", "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", "EEE, dd MMM yyyy HH:mm:ss zzz", "yyyy-MM-dd"))
I would like to see the same behavior for blank value. How do I do it?
Is there a way to configure Jackson to fail for blank value?

You have to implement new deserializer for XMLGregorianCalendar type. It would look like this:
class XMLGregorianCalendarDeserializer extends GregorianCalendarDeserializer {
private static final long serialVersionUID = 1L;
#Override
public XMLGregorianCalendar deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException {
try {
return super.deserialize(jp, ctxt);
} catch (JsonProcessingException e) {
return null;
}
}
}
After that you have to define deserializer in POJO class:
class Foo {
#JsonDeserialize(using = XMLGregorianCalendarDeserializer.class)
private XMLGregorianCalendar dateTime;
...
}

Related

Jackson deserialize date string to Long

Can Java Jackson deserialize a json string date into a Java Long field (milliseconds from epoch)?
This is an example of json field to be deserialized:
"timestamp": "2022-01-02T03:04:05Z",
and this is the same field in the Java class, with the current annotations:
#JsonFormat(shape = JsonFormat.Shape.NUMBER, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX", timezone = "UTC")
#JsonProperty("timestamp")
#JsonPropertyDescription("blah, blah\r\n")
public Long timestamp;
However, an exception happens:
com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot
deserialize value of type java.lang.Long from String
"2022-01-02T06:49:05Z": not a valid Long value
Any hint? Thanks.
The answer by Maurice is correct, it only suffers from using the notoriously troublesome and long outdated SimpleDateFormat and Date classes. Also the deserialize method is much simpler without them:
public class LongTimestampDeserializer extends StdDeserializer<Long> {
public LongTimestampDeserializer() {
this(null);
}
public LongTimestampDeserializer(Class<?> vc) {
super(vc);
}
/** #throws InvalidFormatException If the timestamp cannot be parsed as an Instant */
#Override
public Long deserialize(JsonParser parser, DeserializationContext ctxt)
throws IOException {
String timestamp = parser.getText();
try {
return Instant.parse(timestamp).toEpochMilli();
}
catch (DateTimeParseException dtpe) {
throw new InvalidFormatException(
parser, dtpe.getMessage(), timestamp, Long.class);
}
}
}
The way I understand it the deserializer should throw some subclass of JsonProcessingException in case of a parsing error. InvalidFormatException is a suitable subclass in this case.
Use a custom date deserializer like this one:
public class CustomDateDeserializer extends StdDeserializer<Long> {
private SimpleDateFormat formatter =
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
public CustomDateDeserializer() {
this(null);
}
public CustomDateDeserializer(Class<?> vc) {
super(vc);
}
#Override
public Long deserialize(JsonParser jsonparser, DeserializationContext context)
throws IOException, JsonProcessingException {
String date = jsonparser.getText();
try {
return formatter.parse(date).toInstant().toEpochMilli();
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
}
Next annotate your field with #JsonDeserialize(using = CustomDateDeserializer.class).
#JsonDeserialize(using = CustomDateDeserializer.class)
public Long timestamp;

How i can force JSON dates to not accept integer in java

How i can force JSON date with Java to use a particular pattern and don't accept Integers, for example :
{
"birthday": 1
}
should not be accepted.
I tried
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy")
private LocalDate birthday;
but still accept numbers.
First create a class custom localDate deserializer
public class LocalDateDserializer extends StdDeserializer {
private final SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
public LocalDateDserializer() {
this(null);
}
public LocalDateDserializer(final Class<?> vc) {
super(vc);
}
#Override
public LocalDate deserialize(final JsonParser jsonparser, final DeserializationContext context)
throws IOException, JsonProcessingException {
final String date = jsonparser.getText();
try {
return formatter.parse(date).toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
} catch (final ParseException e) {
throw new RuntimeException(e);
}
}
}
Use the annotation
#JsonDeserialize(using = LocalDateDserializer.class)
private LocalDate birthday;

How to serialize Long epoch date format into date using JSONObject

I have json string payload having date in epoch (long) format.But I have to convert that into yyyyMMddHHmmss format.I'm using custom serializers where I can apply that on particular field.But the serialization is not able to apply on that field.
Test.java
private static String json = "{
"dcCountryCode": "US",
"orderDate": 1517855400000
}";
#JsonSerialize(using = CustomLongSerializer.class)
private static Long date;
public static void main(String[] args) {
JSONObject obj = new JSONObject(json);
String country = obj.getString("dcCountryCode");
date = obj.getLong("orderDate");
System.out.println(country);
System.out.println(date);
}
CustomLongSerializer.java
package com.company;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import java.io.IOException;
import java.text.SimpleDateFormat;
// This is for Custom Date serializer
public class CustomLongSerializer extends StdSerializer<Long> {
protected CustomLongSerializer(Class<Long> t) {
super(t);
}
protected CustomLongSerializer() {
this(Long.class);
}
private static final long serialVersionUID = 1L;
#Override
public void serialize(Long value, JsonGenerator gen, SerializerProvider provider) throws IOException {
SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");
gen.writeString(df.format(value));
}
}
Expected Out put in yyyyMMddHHmmss format.
But still returning epoch date format.
Can anyone help me with this.
It may be implemented in a simpler way by disabling SerializationFeature.WRITE_DATES_AS_TIMESTAMPS and setting DateFormatter in the mapper:
public class TestDate {
private String dcCountryCode;
private Date date;
// getters/setters
}
// test class
String json = "{\n" +
" \"dcCountryCode\": \"US\",\n" +
" \"date\": 1517855400000\n" +
" }";
ObjectMapper mapper = new ObjectMapper()
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
.setDateFormat(new SimpleDateFormat("yyyyMMddHHmmss"));
TestDate test = mapper.readValue(json, TestDate.class);
System.out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(test));
Output:
{
"dcCountryCode" : "US",
"date" : "20180205203000"
}

How to deserialise JSON datetime with nanosecond precision using gson

I'm trying to deserialise a JSON object using gson, but having issues when it comes to dates. The date is deserialised from the JSON object, but because the value in the JSON object is in nanoseconds, the value I get is slightly off of the expected value.
See the following code
JSONClass
public class JSONClass {
private Date timestamp;
public Date getTimestamp() {
return timestamp;
}
public void setTimestamp(Date timestamp) {
this.timestamp = timestamp;
}
}
Main
public class GsonTestApplication {
public static void main(String[] args) {
final Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss.SSS").create();
final String responseJSON = "{ \"timestamp\":\"2017-11-09 11:07:20.079364+00\" }";
final JSONClass foo = gson.fromJson(responseJSON, new TypeToken<JSONClass>(){}.getType());
System.out.println(foo.getTimestamp().toString());
}
}
The output of the application is
Thu Nov 09 11:08:39 GMT 2017
When I expect it to be
Thu Nov 09 11:07:20 GMT 2017
I don't care about the nanosecond precision, so I'm happy for this to be truncated, but as I don't have control over the JSON format, I'm not sure the best way to do this.
How can I get gson to deserialise the date correctly?
This is an issue with the precision available in Date, and with Java 8 it's best to use LocalDateTime. This also means that you will need a TypeAdapter as Gson doesn't work very well with LocalDateTime. This type adapter needs to be registered with Gson to deserialize (and potentially serialize) LocalDateTime objects from Strings.
Something like the below should give you what you need.
JSONClass
public class JSONClass {
private LocalDateTime timestamp;
public LocalDateTime getTimestamp() {
return timestamp;
}
public void setTimestamp(LocalDateTime timestamp) {
this.timestamp = timestamp;
}
}
LocalDateTimeDeserialiser
static class LocalDateTimeDeserializer implements JsonDeserializer<LocalDateTime> {
private DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
LocalDateTimeDeserializer(String datePattern) {
this.formatter = DateTimeFormatter.ofPattern(datePattern);
}
#Override
public LocalDateTime deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
return LocalDateTime.parse(json.getAsString(), formatter);
}
Main
public class GsonTestApplication {
public static void main(String[] args) {
final Gson gson = new GsonBuilder().(LocalDateTime.class, new LocalDateTimeDeserializer("yyyy-MM-dd HH:mm:ss.SSSSSSx")).create();
final String responseJSON = "{ \"timestamp\":\"2017-11-09 11:07:20.079364+00\" }";
final JSONClass foo = gson.fromJson(responseJSON, new TypeToken<JSONClass>(){}.getType());
System.out.println(foo.getTimestamp().toString());
}
}

Jaxb - map attribute of list element

Hy, my xml looks like this:
<forecast>
<time day="2014-06-02">
<symbol></symbol>
</time>
<time day="2014-06-03">
<symbol></symbol>
</time>
</forecast>
I need map day attribute for each "time" object but it looks like it didn't work as I expect.
Heres my classes (java):
public class Forecast {
#XmlElement
public List<WeatherEvent> time;
}
public class WeatherEvent {
#XmlAttribute(name = "day")
#XmlJavaTypeAdapter(DateAdapter.class)
public Date day;
#XmlElement
public Symbol symbol;
}
public class DateAdapter extends XmlAdapter<String, Date> {
private final SimpleDateFormat dateFormat = new SimpleDateFormat(
"yyyy-MM-dd'T'HH:mm:ss");
#Override
public String marshal(Date v) throws Exception {
return dateFormat.format(v);
}
#Override
public Date unmarshal(String v) throws Exception {
Date date = dateFormat.parse(v);
if (date == null) {
SimpleDateFormat simplierFormat = new SimpleDateFormat("yyyy-MM-dd");
date = simplierFormat.parse(v);
}
return date;
}
}
How to map "day" attribute properly to make it not null?
The following line is going to throw a ParseException and exit the method and never get to the logic below when the date looks like: 2014-06-02.
Date date = dateFormat.parse(v);
You will need to catch the exception and ignore it, and then apply the second formatter to it.
Date date = null;
try {
Date date = dateFormat.parse(v);
} catch(ParseException e) {
SimpleDateFormat simplierFormat = new SimpleDateFormat("yyyy-MM-dd");
date = simplierFormat.parse(v);
}
return date;

Categories