Jaxb - map attribute of list element - java

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;

Related

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;

Exception is occurred while trying to compare dates - Nullpointerexception and also get method is returning null value even if i have set the date

NUllpointerexception is occured while trying to compare dates.
I noticed that while debugging that expectedDate and arrivedDate variable value is getting current date time where as i am using set Method to set date.Please correct my code for comparing dates. My method is used to find whether the shipment will arrive on time or before expected or after expected.
public class ShipmentBO {
public void displayStatusOfShipment(Shipment shipment) {
Date expectedDate = new Date();
Date arrivedDate = new Date();
SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy");
Shipment s = new Shipment();
ShipmentStatus SStatus = new ShipmentStatus();
expectedDate = s.getexpectedDeliveryDate();
arrivedDate = SStatus.getarrivedDate();
String s1 = df.format(expectedDate);
String s2 = df.format(arrivedDate);
if (expectedDate.after(arrivedDate)) {
System.out.println("The shipment arrived after the expected date");
} else
if (expectedDate.before(arrivedDate)) {
System.out.println("The shipment arrived before the expected date");
}
}
I am setting the date in the below main class
public static void main(String[] args) throws ParseException {
Scanner sc = new Scanner(System.in);
System.out.println("Enter the shipment details :");
String userDetail = sc.nextLine();
String userDetailParts[] = userDetail.split(",");
//System.out.println(Arrays.toString(userDetailParts));
Shipment shipment = new Shipment();
SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy");
shipment.setid(userDetailParts[0]);
shipment.setsourcePort(userDetailParts[1]);
shipment.setdestinationPort(userDetailParts[2]);
shipment.setexpectedDeliveryDate(sdf.parse(userDetailParts[3]));
shipment.setcustomerName(userDetailParts[4]);
}
And the input I am giving as a comma separated - STAJU01, Hong Kong, Cochin,20-05-2017, karthick
Shipment class:
import java.util.Date;
public class Shipment {
private String id;
private String sourcePort;
private String destinationPort;
private Date expectedDeliveryDate;
private String customerName;
private ShipmentStatus[] shipmentStatus;
public String getid() {
return id;
}
public void setid(String id) {
this.id = id;
}
public String getsourcePort() {
return sourcePort;
}
public void setsourcePort(String sourcePort) {
this.sourcePort = sourcePort;
}
public String getdestinationPort() {
return destinationPort;
}
public void setdestinationPort(String destinationPort) {
this.destinationPort = destinationPort;
}
public Date getexpectedDeliveryDate() {
return expectedDeliveryDate;
}
public void setexpectedDeliveryDate(Date expectedDeliveryDate) {
this.expectedDeliveryDate = expectedDeliveryDate;
}
public String getcustomerName() {
return customerName;
}
public void setcustomerName(String customerName) {
this.customerName = customerName;
}
public Shipment() {
}
public Shipment(String id, String sourcePort, String destinationPort, Date expectedDeliveryDate,
String customerName) {
this.id = id;
this.sourcePort = sourcePort;
this.destinationPort = destinationPort;
this.expectedDeliveryDate = expectedDeliveryDate;
this.customerName = customerName;
}
public ShipmentStatus[] getShipmentStatus() {
return shipmentStatus;
}
public void setShipmentStatus(ShipmentStatus[] shipmentStatus) {
this.shipmentStatus = shipmentStatus;
}
}
ShipmentStatus:
import java.util.Date;
public class ShipmentStatus {
private String arrivalPort;
private String departurePort;
private Date arrivedDate;
private String status;
private Shipment shipment;
public Shipment getshipment() {
return shipment;
}
public void setshipment(Shipment shipment) {
this.shipment = shipment;
}
public String getarrivalPort() {
return arrivalPort;
}
public void setarrivalPort(String arrivalPort) {
this.arrivalPort = arrivalPort;
}
public String getdeparturePort() {
return departurePort;
}
public void setdeparturePort(String departurePort) {
this.departurePort = departurePort;
}
public Date getarrivedDate() {
return arrivedDate;
}
public void setarrivedDate(Date arrivedDate) {
this.arrivedDate = arrivedDate;
}
public String getstatus() {
return status;
}
public void setstatus(String status) {
this.status = status;
}
public ShipmentStatus() {
}
public ShipmentStatus(String arrivalPort, String departurePort, Date arrivedDate, String status,
Shipment shipment) {
this.arrivalPort = arrivalPort;
this.departurePort = departurePort;
this.arrivedDate = arrivedDate;
this.status = status;
this.shipment = shipment;
}
}
You are calling the getexpectedDeliveryDate() on a new Shipment(), but I suspect you want to call it on the shipment you receive as a parameter, which you otherwise leave untouched. Basically, try changing expectedDate = s.getexpectedDeliveryDate(); to expectedDate = shipment.getexpectedDeliveryDate();
See comments in code:
public void displayStatusOfShipment(Shipment shipment) {
// not used:
// SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy");
// extract date from passed shipment instance
Date expectedDate = shipment.getexpectedDeliveryDate();
// I assume ShipmentStatus is part of Shipment so you don't need a new instance here
// ShipmentStatus SStatus = new ShipmentStatus();
// extract arrivalDate from passed shipment's ShipmentStatus:
Date arrivedDate = shipment.getShipmentStatus().getarrivedDate();
// not used :
// String s1 = df.format(expectedDate);
// String s2 = df.format(arrivedDate);
if (expectedDate.after(arrivedDate)) {
System.out.println("The shipment arrived after the expected date");
} else
if (expectedDate.before(arrivedDate)) {
System.out.println("The shipment arrived before the expected date");
}
}
In Shipment add a method which contains the logic to obtain the arrivedDate of the shipment using the ShipmentStatus array. You say it's the third last ShipmentStatus so the code can be:
class Shipment {
public Date getArrivedDate() {
if(shipmentStatus!=null && shipmentStatus.length>=3)
return shipmentStatus[shipmentStatus.length-3].getArrivedDate();
return null;
}
}
I'm not sure if the 3rd last status is a good way to identify the right shipment status. It think it would be better to make your choice based on some business logic. For example:
* first shipment status with arrivedDate!=null
* last shipment status with arrivedDate!=null
* shipmentStatus with status='arrival'
get method is returning null value even if i have set the date
It's because you are not using the Shipment object passed to displayStatusOfShipment(Shipment shipment); rather, you are creating a new instance as follows:
Date expectedDate = new Date();
Shipment s=new Shipment();
expectedDate = s.getexpectedDeliveryDate();
You should replace all the above three lines with the following line:
Date expectedDate = shipment.getexpectedDeliveryDate();
Similarly, to this method, you should pass a parameter of the type, ShipmentStatus for the arrival date and use the same to get the arrival date i.e. you need to replace the following three lines in the similar way as shown above:
Date arrivedDate = new Date();
ShipmentStatus SStatus = new ShipmentStatus();
arrivedDate = SStatus.getarrivedDate();
On comparing dates:
I had suggested you to stop using the poorly designed java.util.Date and to switch to the modern java.time API. This scenario is a perfect example for you to make that switch immediately. Let's see why:
The modern date-time API has a class called, LocalDate which represents just date i.e. it has just three fields: year, month and day and therefore when you compare two instances of LocalDate, only these fields are compared. LocalDate has a rich set of API for comparison e.g. LocalDate#isAfter, LocalDate#isBefore, LocalDate#isEqual etc. which compare the values of these three fields to give you the result.
You will say that java.util.Date also has Date#before, Date#after etc. but this is where the comparison of these methods with those of the LocalDate ends. These methods of java.util.Date compare the milliseconds since January 1, 1970, 00:00:00 GMT i.e. one instance of Date can be before/after even with a difference of a millisecond (unlike two instances of LocalDate which can be before/after only with the difference in one or more of the fields, day, month, and year).
public class Main {
public static void main(String[] args) {
Date date1 = new Date();
Date date2 = new Date();
date2.setTime(date1.getTime() - 1);
System.out.println(date1);
System.out.println(date2);
System.out.println(date1.after(date2));
}
}
Output:
Wed Aug 19 16:34:56 BST 2020
Wed Aug 19 16:34:56 BST 2020
true
If you still insist to use java.util.Date:
If you still insist to use java.util.Date, you will have to get the day, month and year from the two instances of Date yourself and compare them to determine before/after cases. The choice is yours.

How to stop the String from being interpreted as DateTime in Payara?

Good day everyone! I have a get method in rest and the result is stored in something like this:
#XmlRootElement(name = "FooDTO")
public class Foo {
#XmlElement(nillable = true)
private String approvedDate;
private static final DateFormat DEFAULT_DATE_FORMAT = new SimpleDateFormat(
"dd.MM.yyyy");
public Date getApprovedDate() {
try {
return StringUtils.isBlank(approvedDate) ? null
: DEFAULT_DATE_FORMAT.parse(approvedDate);
} catch (ParseException e) {
e.printStackTrace();
return null;
}
}
public void setApprovedDate(Date approvedDate) {
this.approvedDate = approvedDate == null ? "" : DEFAULT_DATE_FORMAT
.format(approvedDate);
}
}
It works in Glassfish server and I get approvedDate as a String in dd.MM.yyyy format. However, in Payara, the String is getting formatted to yyyy-MM-ddTHH:mm:ssZ[Timezone]. How do I adjust this so that the String doesn't get "interpreted" as DateTime? Thanks!
As I see you have a field approvedDate that is private so the the process marshal/unmarshal accesses this field through the get/set, in this case it's returning as Date type on the getApprovedDate method.
Try:
#XmlRootElement(name = "FooDTO")
public class Foo {
#XmlElement(nillable = true)
private String approvedDate;
private static final DateFormat DEFAULT_DATE_FORMAT = new SimpleDateFormat("dd.MM.yyyy");
public String getApprovedDate() {
return approvedDate;
}
public void setApprovedDate(Date approvedDate) {
this.approvedDate = approvedDate == null ? "" : DEFAULT_DATE_FORMAT
.format(approvedDate);
}
}

mysql timestamp displaying in seconds - how to display in java

The timestamp '2015-06-15 13:01:48' which is stored in MySQL database is coming as 1434369708000 in my rest api response. How to handle so that the response also has the same format. I'm using Java, Hibernate, Restful WS with MySQL.
Entity:
private Date CreatedDateTime;
#Column(name = "created_Date_Time", columnDefinition = "TIMESTAMP")
public Date getCreatedDateTime() {
return createdDateTime;
}
public void setCreatedDateTime(Date createdDateTime) {
this.createdDateTime= createdDateTime;
}
JSON View:
#JsonView({MessageView.class})
public Date getCreatedDateTime() {
if (device != null) {
return device.getCreatedDateTime();
}
return null;
}
public void setCreatedDateTime(Date CurrentServerUTC) {
if (device != null) {
this.device.getCreatedDateTime(CurrentServerUTC);
}
}
http://fasterxml.github.io/jackson-annotations/javadoc/2.0.0/com/fasterxml/jackson/annotation/JsonFormat.html
On Entity try annotating CreatedDateTime with:
#JsonFormat(pattern = "yyyy-MM-dd kk:mm:ss")
private Date CreatedDateTime;
Double check the pattern though, I'm not %100 sure it's correct. It's the same as Java SimpleDatePattern string.
PS: non-static field names start with lowercase letter in Java as a convention.
I wrote a simple test to showcase this annotation:
public class JacksonDateTest {
#Test
public void test() throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectMapper om = new ObjectMapper();
om.writeValue(baos, new Sth());
baos.write("\n Sth2 \n".getBytes());
om.writeValue(baos, new Sth2());
System.out.println(baos.toString());
baos.close();
}
public class Sth {
#JsonFormat(pattern = "yyyy-MM-dd kk:mm:ss")
Date creationDate = new Date();
public Date getCreationDate() {
return creationDate;
}
public void setCreationDate(Date creationDate) {
this.creationDate = creationDate;
}
}
public class Sth2 {
Date creationDate = new Date();
public Date getCreationDate() {
return creationDate;
}
public void setCreationDate(Date creationDate) {
this.creationDate = creationDate;
}
}
}
It works. Sth is serialized as:
{"creationDate":"2015-06-16 16:09:06"}
while Sth2 is serialized as:
{"creationDate":1434470946137}
There must be something else going wrong in your code.

Jackson and empty string

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;
...
}

Categories