I'm having problem to convert the xml object using JAXB. The date is in the format Sun Jan 30 16:08:23 BRT 18, but need to convert to the Brazilian format 12-08-2009 16:08:23.
The input is coming in format 2009-08-12 16:08:23 and want output 12-08-2009 16:08:23.
Conversion class JAXB:
public class DateAdapter extends XmlAdapter<String, Date> {
Locale brasil = new Locale("pt", "BR");
private final SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss", brasil);
#Override
public String marshal(Date v) throws Exception {
return dateFormat.format(v);
}
#Override
public Date unmarshal(String v) throws Exception, ParseException {
return dateFormat.parse(v);
}
}
Where i call the Adapter.
#XmlJavaTypeAdapter(DateAdapter.class)
private Date ultima_atualizacaoProduto;
You can't use the same DateFormat both both parsing as well as formatting. Assuming 30 is the Year in your question, here is how you can do it:
public class DateAdapter extends XmlAdapter<String, Date> {
private Locale brasil = new Locale("pt", "BR");
private final SimpleDateFormat SD1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
private final SimpleDateFormat SD2 = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
#Override
public String marshal(Date v) throws Exception {
return SD2.format(v);
}
#Override
public Date unmarshal(String v) throws Exception, ParseException {
return SD1.parse(v);
}
}
Input:
2009-08-12 16:08:23
Output:
12-08-2009 16:08:23
in general only use UTC time for DB and transfer and convert it with java8 java.time.*.
If JDK<8, you can use joda time api.
Related
I am trying to convert XML to java object. In my xml, there is field which looks like:
<pickDisplayTs>2021-09-24T18:03:06.603 +0000</pickDisplayTs>
My Java object looks like the following:
#XmlElement(name = "pickDisplayTs" )
#XmlJavaTypeAdapter(DateAdapter.class)
public Date pickDisplayTs;
My DataAdapter class is the following:
import javax.xml.bind.annotation.adapters.XmlAdapter;
import java.text.SimpleDateFormat;
import java.util.Date;
public class DateAdapter extends XmlAdapter<String, Date> {
private static final String CUSTOM_FORMAT_STRING = "yyyy-MM-
dd'T'HH:mm:ss.SSS'Z'";
#Override
public Date unmarshal(String v) throws Exception {
return new SimpleDateFormat(CUSTOM_FORMAT_STRING).parse(v);
}
#Override
public String marshal(Date v) throws Exception {
return new SimpleDateFormat(CUSTOM_FORMAT_STRING).format(v);
}
}
Code reference: https://github.com/eugenp/tutorials/blob/950bbadc353bdca114befc98cf4a18476352220e/jaxb/src/main/java/com/baeldung/jaxb/dateunmarshalling/DateAdapter.java
This is the method for unmarshalling the xml file:
String filepath = "xml/PickComplete.xml";
String xmlPickComplete = readFromResources(filepath);
PickComp pickCompleteMq = Xml.xmlToObject(xmlPickComplete, PickingSubSystemOrderCompleteMessage.class);
The entire pickCompleteMq is coming to be null but if I am declaring the pickDisplayTs as string, its all good, not sure where I am going wrong. But I need the field to be in Date.
Any help will be appreciated. Thank you.
The problem is with the input. XML extract you have provided doesn't follow the DateAdapter you are using. If you marshal a pojo that contain Date the expected xml tag should be
<pickDisplayTs>2022-02-16T14:02:13.010Z</pickDisplayTs>
Trying to parse the given input gives ParseException.
Code snippet:
Date parse = new SimpleDateFormat(CUSTOM_FORMAT_STRING).parse("2021-09-24T18:03:06.603 +0000");
System.out.println(parse);
Output
java.text.ParseException: Unparseable date: "2021-09-24T18:03:06.603 +0000"
at java.text.DateFormat.parse(DateFormat.java:366)
Solution proposal:
private static final String CUSTOM_FORMAT_STRING = "yyyy-MM-dd'T'HH:mm:ss.SSS Z";
public static void main(String[] args) throws ParseException {
String dateStr = "2021-09-24T18:03:06.603 +0000";
Date marshaledDate = new SimpleDateFormat(CUSTOM_FORMAT_STRING).parse(dateStr);
SimpleDateFormat format = new SimpleDateFormat(CUSTOM_FORMAT_STRING);
format.setTimeZone(TimeZone.getTimeZone("UTC"));
String unmarshalledDate = format.format(marshaledDate);
System.out.println(unmarshalledDate);
}
You can use the above logic in your DataAdapter class as follows:
public class DateAdapter extends XmlAdapter<String, Date> {
private static final String CUSTOM_FORMAT_STRING = "yyyy-MM-dd'T'HH:mm:ss.SSS Z";
#Override
public Date unmarshal(String v) throws Exception {
return new SimpleDateFormat(CUSTOM_FORMAT_STRING).parse(v);
}
#Override
public String marshal(Date v) throws Exception {
SimpleDateFormat sdf = new SimpleDateFormat(CUSTOM_FORMAT_STRING);
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
return sdf.format(v);
}
}
I have the following class that includes a list of Date which I want to mashall. I have created the JaxBAdapter for the Date class, but it seems it is not called.
I think the problem is the fact that I'm using a list and not just a Date variable.
Could you give me some hint on how should I code the adapter or annotate the class so each element of the list is mashalled using the adapter?
Will it work for JSON serialization as well? I'm planning to use this classes on my REST webservice.
Root.java
#XmlRootElement(name = "root")
#XmlAccessorType(XmlAccessType.FIELD)
public class Root {
#XmlElementWrapper(name="timePeriods")
#XmlElement(name = "timePeriod")
#JsonProperty(value = "timePeriod")
#XmlJavaTypeAdapter(value = JaxBDateThreadSafeAdapter.class, type = Date.class)
private List<Date> timePeriod;
public Root() {
this(new ArrayList<String>(), new ArrayList<Date>(2));
}
public Root(List<Date> timePeriod) {
this.timePeriod = new ArrayList<Date>(timePeriod);
}
}
JaxBAdapter
public class JaxBDateThreadSafeAdapter extends XmlAdapter<String, Date> {
/**
* Thread safe {#link DateFormat}.
*/
private static final ThreadLocal<DateFormat> DATE_FORMAT_TL =
new ThreadLocal<DateFormat>() {
#Override
protected DateFormat initialValue() {
// return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// ISO 8601 format
return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
}
};
#Override
public String marshal(Date v) throws Exception {
return DATE_FORMAT_TL.get().format(v);
}
#Override
public Date unmarshal(String v) throws Exception {
return DATE_FORMAT_TL.get().parse(v);
}
}
The #XmlJavaTypeAdapter works as such with java.util.List as well. But the problem in the adapter class is the date format that is used. JAXB (atleast 2.x onwards) is not strict and dont report such errors and quietly suppresses it.
return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
change to
return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
How do I create a custom serialization for java Calendar to json by extending json serializer<Calendar>?
I tried the same for java.until.Date and it's working. In the serialization method, I converted Date as String and write it in json format.
The sample code done for java.util.Date is similar to code given below
public class CDJsonDateSerializer extends JsonSerializer<Date>{
#Override
public void serialize(Date date, JsonGenerator jsonGenerator,SerializerProvider provider)
throws IOException {
SimpleDateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy");
String dateString = dateFormat.format(date);
jsonGenerator.writeString(dateString);
}
}
And it is used like so:
#JsonSerialize(using = CDJsonDateSerializer.class)
private Date startDate;
What can I do for Serialize Calendar in java to json without losing data in Calendar object ?
From JSON to Calendar
Create a JsonSerializer:
public class CalendarSerializer extends JsonSerializer<Calendar> {
private SimpleDateFormat formatter = new SimpleDateFormat("MM/dd/yyyy");
#Override
public void serialize(Calendar calendar, JsonGenerator jsonGenerator,
SerializerProvider serializerProvider) throws IOException {
String dateAsString = formatter.format(calendar.getTime());
jsonGenerator.writeString(dateAsString);
}
}
And then use it:
#JsonSerialize(using = CalendarSerializer.class)
private Calendar calendar;
From Calendar to JSON
Create a JsonDeserializer:
public class CalendarDeserializer extends JsonDeserializer<Calendar> {
private SimpleDateFormat formatter = new SimpleDateFormat("MM/dd/yyyy");
#Override
public Calendar deserialize(JsonParser jsonParser,
DeserializationContext deserializationContext)
throws IOException {
String dateAsString = jsonParser.getText();
try {
Date date = formatter.parse(dateAsString);
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
return calendar;
} catch (Exception e) {
throw new IOException(e);
}
}
And then use it:
#JsonDeserialize(using = CalendarDeserializer.class)
private Calendar calendar;
If the only data you need as json is a date, you can using Gson:
JsonObject albums = new JsonObject();
albums.addProperty("date", new Date());
if your json instead has more than that, you can sonsider to define your own class and serialze it by adding this method to the defined class:
public String toJson() {
Gson gson = new Gson();
return gson.toJson(this);
}
I'm trying to pass a date to a JAX-RS service. Checking other questions like: Date format Mapping to JSON Jackson
The answers and the documentation show that there is a jackson annotation which should allow date formatting.
public class SolutionFilter {
#MatrixParam("toDate")
#JsonFormat(shape=JsonFormat.Shape.STRING, pattern="yyyy-MM-dd", timezone="CET")
private Date toDate;
public void setToDate(Date toDate) {
this.toDate = toDate;
}
}
Upon calling the Rest-Service I'm getting a ParseException:
Caused by: java.text.ParseException: Unparseable date: "2016-01-01"
at java.text.DateFormat.parse(DateFormat.java:366)
at org.glassfish.jersey.message.internal.HttpDateFormat.readDate(HttpDateFormat.java:137)
at org.glassfish.jersey.server.internal.inject.ParamConverters$DateProvider$1.fromString(ParamConverters.java:259)
It seems like the annotation is ignored. Debugging the parse method the pattern is set to EEE, dd MMM yyyy HH:mm:ss zzz and EEE MMM d HH:mm:ss yyyy.
I'm using Spring 4.2.1, Jersey 2.22 which binds jackson 2.5.4.
How can I get the dates parsed with the correct pattern?
Update: Thinking further about it the JSON is only used for output parsing. But this is probably about JAX-RS parameter parsing.
Param conversion is done with ParamConverters. If you follow the stacktrace, you will see the ParamConverters$DateProvider. The next call is the HttpDateFormat class which does the parsing.
If you look at the top of the class, you will see the date formats supported. These are standard HTTP data formats
/**
* The date format pattern for RFC 1123.
*/
private static final String RFC1123_DATE_FORMAT_PATTERN = "EEE, dd MMM yyyy HH:mm:ss zzz";
/**
* The date format pattern for RFC 1036.
*/
private static final String RFC1036_DATE_FORMAT_PATTERN = "EEEE, dd-MMM-yy HH:mm:ss zzz";
/**
* The date format pattern for ANSI C asctime().
*/
private static final String ANSI_C_ASCTIME_DATE_FORMAT_PATTERN = "EEE MMM d HH:mm:ss yyyy";
As far as I know or can tell, there is no configuration available where we can add to this list. The only other option is to write your own converter. For example
#Provider
public class DateParamConverterProvider implements ParamConverterProvider {
private final String format;
public DateParamConverterProvider(String dateFormat) {
this.format = dateFormat;
}
#Override
public <T> ParamConverter<T> getConverter(Class<T> rawType,
Type genericType,
Annotation[] annotations) {
if (rawType != Date.class) { return null; }
return (ParamConverter<T>) new ParamConverter<Date>() {
#Override
public Date fromString(String value) {
SimpleDateFormat formatter = new SimpleDateFormat(format);
try {
return formatter.parse(value);
} catch (Exception ex) {
throw new WebApplicationException("Bad formatted date", 400);
}
}
#Override
public String toString(Date date) {
return new SimpleDateFormat(format).format(date);
}
};
}
}
Here is a complete test case using Jersey Test Framework
public class DateParamTest extends JerseyTest {
private static final String FORMAT = "MM-dd-yyyy";
#Path("date")
public static class DateResource {
#GET
public String get(#MatrixParam("since") Date date) {
return new SimpleDateFormat(FORMAT).format(date);
}
}
#Override
public ResourceConfig configure() {
return new ResourceConfig(DateResource.class)
.register(new DateParamConverterProvider(FORMAT));
}
#Test
public void should_return_same_date_and_format() {
final String date = "09-30-2015";
Response response = target("date").matrixParam("since", date)
.request().get();
assertEquals(200, response.getStatus());
String returnDate = response.readEntity(String.class);
assertEquals(date, returnDate);
System.out.println(returnDate);
}
}
Here is the dependency for the test framework
<dependency>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
<artifactId>jersey-test-framework-provider-grizzly2</artifactId>
<version>${jersey2.version}</version>
<scope>test</scope>
</dependency>
See Also:
Read another parameter within jersey's ParamConverter for another example.
How to in-memory unit test Spring-Jersey for an example of using the test framework with a Spring-Jersey app.
I have a function that uses the current time to make some calculations. I'd like to mock it using mockito.
An example of the class I'd like to test:
public class ClassToTest {
public long getDoubleTime(){
return new Date().getTime()*2;
}
}
I'd like something like:
#Test
public void testDoubleTime(){
mockDateSomeHow(Date.class).when(getTime()).return(30);
assertEquals(60,new ClassToTest().getDoubleTime());
}
Is it possible to mock that? I wouldn't like to change the "tested" code in order to be tested.
The right thing to do is to restructure your code to make it more testable as shown below.
Restructuring your code to remove the direct dependency on Date will allow you to inject different implementations for normal runtime and test runtime:
interface DateTime {
Date getDate();
}
class DateTimeImpl implements DateTime {
#Override
public Date getDate() {
return new Date();
}
}
class MyClass {
private final DateTime dateTime;
// inject your Mock DateTime when testing other wise inject DateTimeImpl
public MyClass(final DateTime dateTime) {
this.dateTime = dateTime;
}
public long getDoubleTime(){
return dateTime.getDate().getTime()*2;
}
}
public class MyClassTest {
private MyClass myClassTest;
#Before
public void setUp() {
final Date date = Mockito.mock(Date.class);
Mockito.when(date.getTime()).thenReturn(30L);
final DateTime dt = Mockito.mock(DateTime.class);
Mockito.when(dt.getDate()).thenReturn(date);
myClassTest = new MyClass(dt);
}
#Test
public void someTest() {
final long doubleTime = myClassTest.getDoubleTime();
assertEquals(60, doubleTime);
}
}
If you have legacy code that you cannot refactor and you do not want to affect System.currentTimeMillis(), try this using Powermock and PowerMockito
//note the static import
import static org.powermock.api.mockito.PowerMockito.whenNew;
#PrepareForTest({ LegacyClassA.class, LegacyClassB.class })
#Before
public void setUp() throws Exception {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
sdf.setTimeZone(TimeZone.getTimeZone("PST"));
Date NOW = sdf.parse("2015-05-23 00:00:00");
// everytime we call new Date() inside a method of any class
// declared in #PrepareForTest we will get the NOW instance
whenNew(Date.class).withNoArguments().thenReturn(NOW);
}
public class LegacyClassA {
public Date getSomeDate() {
return new Date(); //returns NOW
}
}
You could do this by using PowerMock, which augments Mockito to be able to mock static methods. You could then mock System.currentTimeMillis(), which is where new Date() ultimately gets the time from.
You could. I'm not going to advance an opinion on whether you should.
One approach, that does not directly answer the question but might solve the underlying problem (having reproducible tests), is allow the Date as an parameter for tests and add a delegate to the default date.
Like so
public class ClassToTest {
public long getDoubleTime() {
return getDoubleTime(new Date());
}
long getDoubleTime(Date date) { // package visibility for tests
return date.getTime() * 2;
}
}
In production code, you use getDoubleTime() and test against getDoubleTime(Date date).
Working example with new Date() and System.currentTimeMillis() using PowerMockito.
Here is an example for Instance.
#RunWith(PowerMockRunner.class)
#PrepareForTest(LegacyClass.class) // prepares byte-code of the LegacyClass
public class SystemTimeTest {
private final Date fakeNow = Date.from(Instant.parse("2010-12-03T10:15:30.00Z"));
#Before
public void init() throws Exception {
// mock new Date()
PowerMockito.whenNew(Date.class).withNoArguments().thenReturn(fakeNow);
System.out.println("Fake now: " + fakeNow);
// mock System.currentTimeMillis()
PowerMockito.mockStatic(System.class);
PowerMockito.when(System.currentTimeMillis()).thenReturn(fakeNow.getTime());
System.out.println("Fake currentTimeMillis: " + System.currentTimeMillis());
}
#Test
public void legacyClass() {
LegacyClass legacyClass = new LegacyClass();
legacyClass.methodWithNewDate();
legacyClass.methodWithCurrentTimeMillis();
}
}
class LegacyClass {
public void methodWithNewDate() {
Date now = new Date();
System.out.println("LegacyClass new Date() is " + now);
}
public void methodWithCurrentTimeMillis() {
long now = System.currentTimeMillis();
System.out.println("LegacyClass System.currentTimeMillis() is " + now);
}
}
Console output
Fake now: Fri Dec 03 16:15:30 NOVT 2010
Fake currentTimeMillis: 1291371330000
LegacyClass new Date() is Fri Dec 03 16:15:30 NOVT 2010
LegacyClass System.currentTimeMillis() is 1291371330000
you can also use jmockit to mock new Date():
#Test
public void mockTime() {
new MockUp<System>() {
#Mock
public long currentTimeMillis() {
// Now is always 11/11/2021
Date fake = new Date(121, Calendar.DECEMBER, 11);
return fake.getTime();
}
};
Assert.assertEquals("mock time failed", new Date(121, Calendar.DECEMBER, 11), new Date());
}
Date now = new Date();
now.set(2018, Calendar.FEBRUARY, 15, 1, 0); // set date to 2018-02-15
//set current time to 2018-02-15
mockCurrentTime(now.getTimeInMillis());
private void mockCurrentTime(long currTimeUTC) throws Exception {
// mock new dates with current time
PowerMockito.mockStatic(Date.class);
PowerMockito.whenNew(Date.class).withNoArguments().thenAnswer(new Answer<Date>() {
#Override
public Date answer(InvocationOnMock invocation) throws Throwable {
return new Date(currTimeUTC);
}
});
//do not mock creation of specific dates
PowerMockito.whenNew(Date.class).withArguments(anyLong()).thenAnswer(new Answer<Date>() {
#Override
public Date answer(InvocationOnMock invocation) throws Throwable {
return new Date((long) invocation.getArguments()[0]);
}
});
// mock new calendars created with time zone
PowerMockito.mockStatic(Calendar.class);
Mockito.when(Calendar.getInstance(any(TimeZone.class))).thenAnswer(new Answer<Calendar>() {
#Override
public Calendar answer(InvocationOnMock invocation) throws Throwable {
TimeZone tz = invocation.getArgumentAt(0, TimeZone.class);
Calendar cal = Calendar.getInstance(tz);
cal.setTimeInMillis(currTimeUTC);
return cal;
}
});
}