Element in JSON of type date is being sent as timestamp instead of date - json

I am converting the json received from database to object User.class using Objectmapper,class structure as follows
#XmlRootElement
public class User {
public User() {
super();
}
#XmlElement
private String username=null;
#XmlElement
private Feedbacks feedbacks=null;
//getter setter....
}
User has an instance of Feedbacks class which in turn has Arraylist< Feedback>
#XmlRootElement
public class Feedbacks {
public Feedbacks() {
}
#XmlElement
private ArrayList<Feedback> FeedbackList=new ArrayList<>();
public ArrayList<Feedback> getFeedbackList() {
return FeedbackList;
}
public void setFeedbackList(ArrayList<Feedback> FeedbackList) {
this.FeedbackList = FeedbackList;
}
}
and
public class Feedback {
private String feedback=null;
//private String timeStamp=null;
/*#JsonDeserialize(using = DateDeserializer.class); */
#JsonFormat(pattern = "MM/dd/yyyy HH:mm")
private Date feedbackDate;
public Feedback (){}
}
Sample json that i retrieve from db is:
{
"userName":"Test",
"feedbacks":{
"feedbackTOList":[
{
"feedback":"Please select appropriate value..1",
"timeStamp":"03/01/2000 14:52"
},
{
"feedback":"Please select appropriate value..2",
"timeStamp":"03/01/2018 13:50"
},
{
"feedback":"Please select appropriate value..3",
"timeStamp":"02/01/2018 10:52"
}
]
}
}
Json to object conversion and sorting the list of feedback based on date:
SimpleDateFormat formatter = new SimpleDateFormat("MM/dd/yyyy HH:mm");
mapper = new ObjectMapper();
mapper.setDateFormat(formatter);
userObject= mapper.readValue(jsonPayload, User.class);
Collections.sort(user.getFeedbacks().getFeedbackList(), new
Comparator<Feedback>() {
public int compare(Feedback f1, Feedback f2) {
if (f1.getTimeStamp() == null || f2.getTimeStamp() == null)
return 0;
return (f2.getTimeStamp().compareTo(f1.getTimeStamp()));
}
});
The issue is when angular UI consumes the object from rest service,instead of Date (Wed Aug 01 16:20:00 EDT 2018]) the timestamp value is being sent as timeStamp=1533154800000.
My question is how do i send the string in given format or atleast date object but not timestamp?
So far i tried #JsonFormat(pattern = "MM/dd/yyyy HH:mm"),custom date deserializer but no luck(by referring many other posts on stackoverflow,sites),can some one please let me know what mistake am i making?

Related

How to pass header information to a custom Jackson JSON deserializer

I have a custom Jackson JSON deserializer that converts a user's local timestamp to UTC:
Jackson Config:
#Configuration
public class JacksonConfiguration {
#Bean
public Module javaTimeModule() {
JavaTimeModule module = new JavaTimeModule();
module.addDeserializer(LocalDateTime.class, new JsonDatetimeDeserializer());
module.addSerializer(LocalDateTime.class, new JsonDatetimeSerializer());
return module;
}
}
Deserializer:
public class JsonDatetimeDeserializer extends JsonDeserializer<LocalDateTime> {
#Override
public LocalDateTime deserialize(JsonParser jsonParser, DeserializationContext deserializationContext)
throws IOException {
String timestamp = jsonParser.getText();
DateTimeFormatConstant dateTimeFormat = findDateTimeFormat(timestamp);
LocalDateTime deserializedTime = null;
if (dateTimeFormat != null) {
switch (dateTimeFormat) {
case ISO -> deserializedTime = localTimeToUtc(timestamp, dateTimeFormat.value());
case ISO_WITH_OFFSET -> deserializedTime = zonedTimeWithOffsetToUtc(timestamp, dateTimeFormat.value());
//Add more cases here as more formats are supported and added to DateTimeFormatConstant
}
}
return deserializedTime;
}
private DateTimeFormatConstant findDateTimeFormat(String timestamp) {
DateTimeFormatConstant currentFormat = null;
for (DateTimeFormatConstant format : DateTimeFormatConstant.values()) {
boolean isValidFormat = validateFormat(format.value(), timestamp);
if (isValidFormat) {
currentFormat = format;
break;
}
}
return currentFormat;
}
private boolean validateFormat(String format, String time) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format);
boolean isValid;
try {
try {
LocalDateTime localDateTime = LocalDateTime.parse(time, formatter);
localDateTime.format(formatter);
isValid = true;
} catch (DateTimeException e) {
ZonedDateTime zonedDateTime = ZonedDateTime.parse(time, formatter);
zonedDateTime.format(formatter);
isValid = true;
}
} catch (DateTimeParseException e) {
isValid = false;
}
return isValid;
}
private LocalDateTime zonedTimeWithOffsetToUtc(String time, String format) {
ZonedDateTime zonedDateTime = ZonedDateTime.parse(time, DateTimeFormatter.ofPattern(format));
ZonedDateTime utcTime = zonedDateTime.withZoneSameInstant(ZoneOffset.UTC);
return utcTime.toLocalDateTime();
}
private LocalDateTime localTimeToUtc(String time, String format) {
AuthenticationDetails authDetails = (AuthenticationDetails) SecurityContextHolder.getContext().getAuthentication().getDetails();
ZoneId zoneId = authDetails.getZoneId();
LocalDateTime localDateTime = LocalDateTime.parse(time, DateTimeFormatter.ofPattern(format));
Instant instant = localDateTime.atZone(zoneId).toInstant();
ZonedDateTime zonedDateTimeUTC = ZonedDateTime.ofInstant(instant, ZoneId.of("Etc/UTC"));
return zonedDateTimeUTC.toLocalDateTime();
}
}
I want to add a new case to this method whereby the user provides a time zone offset in the request headers. I'm not sure how I can pass a header from my controller to the custom deserializer. Any thoughts?
So far, I've looked into using ThreadLocal but I fear that may be dangerous in the future when multiple requests are being processed on the same thread.

Spring - How to convert a String representation of Date in JSON body field in request model comming into controller to OffsetDateTime

I have legacy data coming in to my API as part of UserRequest model like
#PostMapping
public MyResponse saveUser(#Valid #RequestBody UserRequest userRequest) {
...
}
UserRequest class uses OffsetDateTime for dateRegistered field:
public class UserRequest {
...
OffsetDateTime birthDate;
...
}
The problem I am having is that the data is coming into the API using below format for dateRegistered field:
{
"last-name":"Mikel",
"birth-date":"20200716"
}
So, the string representation "20200716" from JSON request needs to be somehow converted to OffsetDateTime like "2020-07-16T00:00:00Z" (the time portion and offset is set to 0s).
By default, the "20200716":
{
"last-name":"Mikel",
"birth-date":"20200716"
}
, gets converted to OffsetDateTime value of
{
"last-name": "Mikel",
"birth-date": "1970-08-22T19:18:36Z"
}
, which is obviously way off.
How do I convert a string representation of date in Json field like "20200716" to its OffsetDateTime representation like "2020-07-16T00:00:00Z" or "2020-07-16T00:00:00.000+00:00"?
I was trying to annotate my OffsetDateTime field with #JsonFormat("yyyyMMdd") but that is throwing exception like: JSON parse error: Cannot deserialize value of type java.time.OffsetDateTime from String "20200716".
you don't need a JSON annotation. You need to adjust the setter as follow.
public class MedicalCandidateRequest {
private OffsetDateTime dateRegistered;
public OffsetDateTime getDateRegistered() {
return dateRegistered;
}
public void setDateRegistered(String dateString) {
final String pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSxx";
DateTimeFormatter dtFormatter = DateTimeFormatter.ofPattern(pattern);
this.dateRegistered = OffsetDateTime.parse(dateString, dtFormatter );
}
}
Change the parameter of the setter method to a String and do the conversion yourself.
public void setDateRegistered(String value) {
this.dateRegistered = doConversionHere(value);
}
Thanks for suggestions but I have decided to go with my own implementation.
I provided a custom deserializer like:
public class CustomDateDeserializer extends JsonDeserializer<OffsetDateTime> {
private static final String PATTERN = "yyyyMMdd";
private final DateTimeFormatter formatter;
public CustomDateDeserializer() {
this.formatter = DateTimeFormatter.ofPattern(PATTERN);
}
#Override
public OffsetDateTime deserialize(JsonParser parser, DeserializationContext context) throws IOException, JacksonException {
LocalDate localDate = LocalDate.parse(parser.getText), formatter);
OffsetDateTime offsetDateTime = OffsetDateTime.of(localDate, LocalTime.MIDNIGHT, ZoneOffset.UTC);
return offsetDateTime;
}
}
, which I then use to annotate my model field like:
#JsonDeserialize(using = CustomDateDeserializer.class)
private OffsetDateTime birthDate;

How to serialize multiple DateTime properties in the same List of object using different formats for each one? [duplicate]

I have a class with two DateTime properties. I need to serialize each of the properties with a different format. How can I do it? I tried:
JsonConvert.SerializeObject(obj, Formatting.None,
new IsoDateTimeConverter {DateTimeFormat = "MM.dd.yyyy"});
This solution doesn't work for me because it applies date format to all the properties. Is there any way to serialize each DateTime property with different format? Maybe there is some attribute?
A straightforward way to handle this situation is to subclass the IsoDateTimeConverter to create a custom date converter for each different date format that you need. For example:
class MonthDayYearDateConverter : IsoDateTimeConverter
{
public MonthDayYearDateConverter()
{
DateTimeFormat = "MM.dd.yyyy";
}
}
class LongDateConverter : IsoDateTimeConverter
{
public LongDateConverter()
{
DateTimeFormat = "MMMM dd, yyyy";
}
}
Then you can use the [JsonConverter] attribute to decorate the individual DateTime properties in any classes which need custom formatting:
class Foo
{
[JsonConverter(typeof(MonthDayYearDateConverter))]
public DateTime Date1 { get; set; }
[JsonConverter(typeof(LongDateConverter))]
public DateTime Date2 { get; set; }
// Use default formatting
public DateTime Date3 { get; set; }
}
Demo:
Foo foo = new Foo
{
Date1 = DateTime.Now,
Date2 = DateTime.Now,
Date3 = DateTime.Now
};
string json = JsonConvert.SerializeObject(foo, Formatting.Indented);
Console.WriteLine(json);
Output:
{
"Date1": "03.03.2014",
"Date2": "March 03, 2014",
"Date3": "2014-03-03T10:25:49.8885852-06:00"
}
NewtonSoft.Json has a structure that's a bit difficult to understand, you can use something like the following custom converter to do what you want:
[TestMethod]
public void Conversion()
{
var obj = new DualDate()
{
DateOne = new DateTime(2013, 07, 25),
DateTwo = new DateTime(2013, 07, 25)
};
Assert.AreEqual("{\"DateOne\":\"07.25.2013\",\"DateTwo\":\"2013-07-25T00:00:00\"}",
JsonConvert.SerializeObject(obj, Formatting.None, new DualDateJsonConverter()));
}
class DualDate
{
public DateTime DateOne { get; set; }
public DateTime DateTwo { get; set; }
}
class DualDateJsonConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
JObject result = new JObject();
DualDate dd = (DualDate)value;
result.Add("DateOne", JToken.FromObject(dd.DateOne.ToString("MM.dd.yyyy")));
result.Add("DateTwo", JToken.FromObject(dd.DateTwo));
result.WriteTo(writer);
}
// Other JsonConverterMethods
public override bool CanConvert(Type objectType)
{
return objectType == typeof(DualDate);
}
public override bool CanWrite
{
get
{
return true;
}
}
public override bool CanRead
{
get
{
return false;
}
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
You can create a custom date class that inherits the IsoDateTimeConverter and pass a format on the constructor. On the attributes, you can specify which format corresponds to each property. See code below:
public class LoginResponse
{
[JsonProperty("access_token")]
public string AccessToken { get; set; }
[JsonProperty("token_type")]
public string TokenType { get; set; }
[JsonProperty("expires_in")]
public DateTime ExpiresIn { get; set; }
[JsonProperty("userName")]
public string Username { get; set; }
[JsonConverter(typeof(CustomDateFormat), "EEE, dd MMM yyyy HH:mm:ss zzz")]
[JsonProperty(".issued")]
public DateTime Issued { get; set; }
[JsonConverter(typeof(CustomDateFormat), "MMMM dd, yyyy")]
[JsonProperty(".expires")]
public DateTime Expires { get; set; }
}
public class CustomDateFormat : IsoDateTimeConverter
{
public CustomDateFormat(string format)
{
DateTimeFormat = format;
}
}
I realise this is an old question, but I stumbled upon it during my search for same question.
Newtonsoft has now a DateFormatString property in JsonSerializerSettings class which you can use. I came to this question looking for answer and have just found the property, I have used it as below and it works as below:
private const string _StrDateFormat = "yyyy-MM-dd HH:mm:ss";
private static string GetJSON(object value)
{
return JsonConvert.SerializeObject(value, new JsonSerializerSettings
{
DateFormatString = _StrDateFormat
});
}
When value will have a DateTime object, it will convert it into string respecting _StrDateFormat string.
Perhaps this official link can be updated?
Regards.

jackson - how to serialize nested object with custom object names?

Here is an example:
class Person {
String name;
Address addressGiven;
//getters and setters
class Address {
#JsonProperty(name="stno")
private String StreetNo
#JsonProperty(name="type")
private AddressType addType;
public void setstno(String stno){
if (this.addressGiven==null)
addressGiven=new Address();
addressGiven.setStno(stno);
}
public void setType(String type) {
if (addressGiven==null){
addressGiven=new Address();
}
addressGiven.setType(AddressType.valueOf(type));
}
// other getters and setters
}
}
AddressType.java
Enum AddressType {
HOME,
OFFICE,
BUSINESS,
DEFAULT;
}
Two points to note before I go to my question:
Address in an inner class
the instance attribute addType is of enum type
when I serialize the object:
Person person = new Person();
Person.setStNo("1234");
person.setType("HOME");
ObjectMapper mapper = new ObjectMapper();
String body = mapper.writeValueAsString(person);
System.out.println(body);
I expect:
{
"addressGiven:
{ "stno" : "1234",
"type" : HOME,
}
}
but what I get is this :
{ "streetNo" : "1234"}.
Three noticable differences
nested json is missing
streetNo but not stno is returned
No addressType is present.
why is the expected json (i.e inner not returned. am I missing some annotations anywhere?
I browsed through jackson docs. but could not figure out sooner. so here I am?
Jackson will automatically call the empty constructor on the object is serializing. the exception being if a constructor is annotated with #JsonCreator, or a builder class annotated with #JsonPOJOBuilder, and maybe another one im missing. i would remove the creation of Address and also the checking for null. dummy down those setters/getters.
ObjecMapper by default handles serialization of an Enum. i would suggest removing that manual conversion
#see DeserializationFeature.READ_ENUMS_USING_TO_STRING. default value is false which means that it uses Enum.valueOf to serialize the String into the correct value.
with all that being said, you are expecting something that doesnt match your code. Person does not have an attribute type, nor stNo. those are Address attributes. im curious to know how you get the output shown. see below for code and example output
class Person {
private String name;
private Address addressGiven;
public void setName(String name) { this.name = name; }
public void setAddressGiven(Address addressGiven) { this.addressGiven = addressGiven; }
public String getName() { return name; }
public Address getAddressGiven() { return addressGiven; }
enum AddressType { HOME, OFFICE, BUSINESS, DEFAULT }
static class Address {
#JsonProperty("stno") private String streetNo;
#JsonProperty("type") private AddressType addType;
public String getStreetNo() { return streetNo; }
public void setStreetNo(String streetNo) { this.streetNo = streetNo; }
public AddressType getAddType() { return addType; }
public void setAddType(AddressType addType) { this.addType = addType;}
}
public static void main(String[] args) throws JsonProcessingException {
Person person = new Person();
person.name = "joe";
Address address = new Address();
address.addType = AddressType.BUSINESS;
address.streetNo = "010101";
person.addressGiven = address;
ObjectMapper mapper = new ObjectMapper();
String body = mapper.writeValueAsString(person);
System.out.println(body);
}
}
{"name":"joe","addressGiven":{"stno":"010101","type":"BUSINESS"}}

Jackson JSON Marshall ignore getter

Im using Jackson to convert a POJO into a JSON to store in the DB. However I have a getter that I want to ignore. I have see a lot of info relating to #JsonIgnoreProperties but I can't seem to make any progress with it. I basically want the equivalent of #Transient.
Basic usecase (I want to ignore the InternationalNumber):
public class PhoneNumber {
private String country;
private String number;
public PhoneNumber() {}
public String getCountry() {
return country;
}
public String getLocalNumber() {
return localNumber;
}
public String getInternationalNumber() {
String result = "Not Available";
if (country != null && localNumber != null) {
result = new PhoneNumberHandler().internationalFormat(
localNumber, WorldCountries.countryToIso2Code(country));
}
return result;
}
}
That would be #JsonIgnore on getter method.