Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change date/time format to ISO8601 on JSON serialization #548

Merged
merged 7 commits into from
Jul 31, 2013
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package br.com.caelum.vraptor.deserialization.gson;

import java.util.List;

import javax.servlet.ServletContext;

import br.com.caelum.vraptor.config.BasicConfiguration;
import br.com.caelum.vraptor.ioc.Component;
import br.com.caelum.vraptor.util.ISO8601Util;

import com.google.common.collect.Lists;
import com.google.gson.JsonDeserializer;

@SuppressWarnings("rawtypes")
@Component
public class DefaultJsonDeserializers implements JsonDeserializers {

private List<JsonDeserializer> deserializers;

public DefaultJsonDeserializers(List<JsonDeserializer> deserializers, ServletContext servletContext) {
this.deserializers = Lists.newArrayList(deserializers);

String packagesParam = servletContext.getInitParameter(BasicConfiguration.BASE_PACKAGES_PARAMETER_NAME);
if ((packagesParam != null) && (packagesParam.contains("br.com.caelum.vraptor.serialization.gson.adapters.iso8601"))) {
this.deserializers.add(new br.com.caelum.vraptor.serialization.gson.adapters.iso8601.CalendarDeserializer(new ISO8601Util()));
this.deserializers.add(new br.com.caelum.vraptor.serialization.gson.adapters.iso8601.DateDeserializer(new ISO8601Util()));
}
}

public List<JsonDeserializer> getDeserializers() {
return deserializers;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,9 @@ public class GsonDeserialization implements Deserializer {

private final HttpServletRequest request;

public GsonDeserialization(ParameterNameProvider paramNameProvider, List<JsonDeserializer> adapters, HttpServletRequest request) {
public GsonDeserialization(ParameterNameProvider paramNameProvider, JsonDeserializers adapters, HttpServletRequest request) {
this.paramNameProvider = paramNameProvider;
this.adapters = adapters;
this.adapters = adapters.getDeserializers();
this.request = request;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package br.com.caelum.vraptor.deserialization.gson;

import java.util.List;

import com.google.gson.JsonDeserializer;

public interface JsonDeserializers {

@SuppressWarnings("rawtypes")
List<JsonDeserializer> getDeserializers();
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,18 @@

import java.util.List;

import javax.servlet.ServletContext;

import br.com.caelum.vraptor.config.BasicConfiguration;
import br.com.caelum.vraptor.ioc.Component;
import br.com.caelum.vraptor.serialization.gson.adapters.HibernateProxySerializer;
import br.com.caelum.vraptor.util.ISO8601Util;

import com.google.common.collect.Lists;
import com.google.gson.JsonSerializer;

@Component
@SuppressWarnings("rawtypes")
public class DefaultJsonSerializers implements JsonSerializers {

private static boolean isHibernateProxyPresent;
Expand All @@ -22,16 +27,19 @@ public class DefaultJsonSerializers implements JsonSerializers {
}
private List<JsonSerializer> serializers;

@SuppressWarnings("rawtypes")
public DefaultJsonSerializers(List<JsonSerializer> serializers) {
public DefaultJsonSerializers(List<JsonSerializer> serializers, ServletContext servletContext) {
this.serializers = Lists.newArrayList(serializers);
if (isHibernateProxyPresent) {
if (isHibernateProxyPresent)
this.serializers.add(new HibernateProxySerializer());

String packagesParam = servletContext.getInitParameter(BasicConfiguration.BASE_PACKAGES_PARAMETER_NAME);
if ((packagesParam != null) && (packagesParam.contains("br.com.caelum.vraptor.serialization.gson.adapters.iso8601"))) {
this.serializers.add(new br.com.caelum.vraptor.serialization.gson.adapters.iso8601.CalendarSerializer(new ISO8601Util()));
this.serializers.add(new br.com.caelum.vraptor.serialization.gson.adapters.iso8601.DateSerializer(new ISO8601Util()));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm sorry to insist, I really don't like this solution =(

Proposal: move this serializers to package br.com.caelum.vraptor.serialization.iso8601.gson, add @Component and see if everything works.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are not the only one! With your suggestion the behavior changed:

Without context-param:
The VRaptor instances only one of the CalendarSerializers or CalendarDeserializer from gson.adapters package. Fine!

With context-param (weird):
The VRaptor instances duplicate instances of gson.adapters.CalendarSerializer when invokes serialization (oh noes!), but duplicate instances of iso8601.gson.CalendarDeserializer when invokes consumes (ok, fine!).

This weird behavior is caused by guice provider? I think I am not able to fix this without help =(

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are you sure?

Isn't it one gson.adapters.CalendarSerializer and one iso8601.gson.CalendarSerializer?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Components with the same name won't work =/ Guice provider stores them by simple name in a request attribute...

You should change the calendar serializer name =)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bad news yet =(

Without context-param works fine!

With context-param, the order the order of the components alter the product:

Works fine because the serializers are listed at the end:
[br.com.caelum.vraptor.serialization.gson.adapters.CalendarSerializer@75679fa7,
br.com.caelum.vraptor.serialization.gson.adapters.MessageSerializer@6622c928,
br.com.caelum.vraptor.serialization.iso8601.gson.DateISO8601Serializer@75c4957a,
br.com.caelum.vraptor.serialization.iso8601.gson.CalendarISO8601Serializer@3d31b8fd]

Don´t work because the deserializers are listed at the top:
[br.com.caelum.vraptor.serialization.iso8601.gson.DateISO8601Deserializer@4d1c488c,
br.com.caelum.vraptor.serialization.iso8601.gson.CalendarISO8601Deserializer@7bdf06e0,
br.com.caelum.vraptor.serialization.gson.adapters.CalendarDeserializer@6f3c9ba8]

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

have you tried to change the packages order declaration on web.xml?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I've tried before, and the result is the same =/

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Eventually the log can help:

  • The order of these registrations occurs regardless of the packages order declaration on web.xml
08:19:30,543 DEBUG [GuiceComponentRegistry] Binding class br.com.caelum.vraptor.serialization.gson.adapters.CalendarSerializer to class br.com.caelum.vraptor.serialization.gson.adapters.CalendarSerializer 08:19:30,546 DEBUG [GuiceComponentRegistry] Ignoring binding of class br.com.caelum.vraptor.serialization.gson.adapters.CalendarSerializer to class br.com.caelum.vraptor.serialization.gson.adapters.CalendarSerializer 08:19:30,546 DEBUG [GuiceComponentRegistry] Binding interface com.google.gson.JsonSerializer to class br.com.caelum.vraptor.serialization.gson.adapters.CalendarSerializer 08:19:30,704 DEBUG [GuiceComponentRegistry] Binding class br.com.caelum.vraptor.serialization.iso8601.gson.CalendarISO8601Deserializer to br.com.caelum.vraptor.serialization.iso8601.gson.CalendarISO8601Deserializer 08:19:30,706 DEBUG [GuiceComponentRegistry] Ignoring binding of class br.com.caelum.vraptor.serialization.iso8601.gson.CalendarISO8601Deserializer to br.com.caelum.vraptor.serialization.iso8601.gson.CalendarISO8601Deserializer 08:19:30,706 DEBUG [GuiceComponentRegistry] Ignoring binding of interface com.google.gson.JsonDeserializer to class br.com.caelum.vraptor.serialization.iso8601.gson.CalendarISO8601Deserializer 08:19:30,767 DEBUG [GuiceComponentRegistry] Binding class br.com.caelum.vraptor.serialization.gson.adapters.CalendarDeserializer to class br.com.caelum.vraptor.serialization.gson.adapters.CalendarDeserializer 08:19:30,769 DEBUG [GuiceComponentRegistry] Ignoring binding of class br.com.caelum.vraptor.serialization.gson.adapters.CalendarDeserializer to class br.com.caelum.vraptor.serialization.gson.adapters.CalendarDeserializer 08:19:30,769 DEBUG [GuiceComponentRegistry] Ignoring binding of interface com.google.gson.JsonDeserializer to class br.com.caelum.vraptor.serialization.gson.adapters.CalendarDeserializer 08:19:30,780 DEBUG [GuiceComponentRegistry] Binding class br.com.caelum.vraptor.serialization.iso8601.gson.CalendarISO8601Serializer to class br.com.caelum.vraptor.serialization.iso8601.gson.CalendarISO8601Serializer 08:19:30,781 DEBUG [GuiceComponentRegistry] Ignoring binding of class br.com.caelum.vraptor.serialization.iso8601.gson.CalendarISO8601Serializer to class br.com.caelum.vraptor.serialization.iso8601.gson.CalendarISO8601Serializer 08:19:30,781 DEBUG [GuiceComponentRegistry] Ignoring binding of interface com.google.gson.JsonSerializer to class br.com.caelum.vraptor.serialization.iso8601.gson.CalendarISO8601Serializer 08:19:30,876 DEBUG [ScopeLifecycleListener] Registering lifecycle listeners for br.com.caelum.vraptor.serialization.gson.adapters.CalendarSerializer 08:19:30,895 DEBUG [ScopeLifecycleListener] Registering lifecycle listeners for br.com.caelum.vraptor.serialization.iso8601.gson.CalendarISO8601Deserializer 08:19:30,900 DEBUG [ScopeLifecycleListener] Registering lifecycle listeners for br.com.caelum.vraptor.serialization.gson.adapters.CalendarDeserializer 08:19:30,902 DEBUG [ScopeLifecycleListener] Registering lifecycle listeners for br.com.caelum.vraptor.serialization.iso8601.gson.CalendarISO8601Serializer

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}
}

public List<JsonSerializer> getSerializers() {
return serializers;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package br.com.caelum.vraptor.serialization.gson.adapters.iso8601;

import java.lang.reflect.Type;
import java.text.ParseException;
import java.util.Calendar;

import br.com.caelum.vraptor.converter.ConversionError;
import br.com.caelum.vraptor.util.ISO8601Util;

import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;

public class CalendarDeserializer implements JsonDeserializer<Calendar> {

private final ISO8601Util iso8601;

public CalendarDeserializer(ISO8601Util iso8601) {
this.iso8601 = iso8601;
}

public Calendar deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {

try {
String value = json.getAsString();

Calendar calendar = iso8601.toCalendar(value);

return calendar;
} catch (ParseException e) {
throw new ConversionError("Error to convert Calendar: " + e.getMessage());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package br.com.caelum.vraptor.serialization.gson.adapters.iso8601;

import java.lang.reflect.Type;
import java.util.Calendar;

import br.com.caelum.vraptor.util.ISO8601Util;

import com.google.gson.JsonElement;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;

public class CalendarSerializer implements JsonSerializer<Calendar> {

private final ISO8601Util iso8601;

public CalendarSerializer(ISO8601Util iso8601) {
this.iso8601 = iso8601;
}

public JsonElement serialize(Calendar calendar, Type typeOfSrc, JsonSerializationContext context) {

String json = iso8601.fromCalendar(calendar);

return new JsonPrimitive(json);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package br.com.caelum.vraptor.serialization.gson.adapters.iso8601;

import java.lang.reflect.Type;
import java.text.ParseException;
import java.util.Date;

import br.com.caelum.vraptor.converter.ConversionError;
import br.com.caelum.vraptor.util.ISO8601Util;

import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;

public class DateDeserializer implements JsonDeserializer<Date> {

private final ISO8601Util iso8601;

public DateDeserializer(ISO8601Util iso8601) {
this.iso8601 = iso8601;
}

public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {

try {
String value = json.getAsString();

Date date = iso8601.toDate(value);

return date;
} catch (ParseException e) {
throw new ConversionError("Error to convert Date: " + e.getMessage());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package br.com.caelum.vraptor.serialization.gson.adapters.iso8601;

import java.lang.reflect.Type;
import java.util.Date;

import br.com.caelum.vraptor.util.ISO8601Util;

import com.google.gson.JsonElement;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;

public class DateSerializer implements JsonSerializer<Date> {

private final ISO8601Util iso8601;

public DateSerializer(ISO8601Util iso8601) {
this.iso8601 = iso8601;
}

public JsonElement serialize(Date date, Type typeOfSrc, JsonSerializationContext context) {

String json = iso8601.fromDate(date);

return new JsonPrimitive(json);
}
}
104 changes: 104 additions & 0 deletions vraptor-core/src/main/java/br/com/caelum/vraptor/util/ISO8601Util.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@

/***
* Copyright (c) 2009 Caelum - www.caelum.com.br/opensource
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package br.com.caelum.vraptor.util;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.TimeZone;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import br.com.caelum.vraptor.ioc.Component;

/**
* Helper class for handling ISO8601 strings of the following format:
* "1982-06-10T05:00:00.000-03:00". It also supports parsing the "Z" timezone.
*
* @author Rafael Dipold
*/
@Component
public final class ISO8601Util {

/** Default Extended Format */
private static final String DEFAULT_ISO8601_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSZ";

private static final String REGEX_ISO8601 = "^(\\d{4})-?(\\d\\d)-?(\\d\\d)(?:T(\\d\\d)(?::?(\\d\\d)(?::?(\\d\\d)(?:\\.(\\d+))?)?)?(Z|([+-])(\\d\\d):?(\\d\\d)?)?)?$";
// 1 2 3 4 5 6 7 8 9 10 11

private static final SimpleDateFormat formatter = new SimpleDateFormat(DEFAULT_ISO8601_FORMAT);

/** Transform Calendar to ISO8601 string. */
public String fromCalendar(final Calendar calendar) {
formatter.setTimeZone(calendar.getTimeZone());
return fromDate(calendar.getTime());
}

/** Transform java.util.Date to ISO8601 string. */
public String fromDate(final Date date) {
String formatted = formatter.format(date);
formatted = formatted.replaceAll("[+-]00:?00$", "Z");
return formatted;
}

/** Get current date and time formatted as ISO8601 string. */
public String now() {
return fromCalendar(GregorianCalendar.getInstance());
}

/** Transform ISO8601 string to Calendar
* @throws ParseException */
public Calendar toCalendar(final String iso8601String) throws ParseException {
Pattern pattern = Pattern.compile(REGEX_ISO8601);
Matcher matcher = pattern.matcher(iso8601String);

if (matcher.matches()) {
int year = matcher.group(1) != null ? Integer.valueOf(matcher.group(1)) : 0;
int month = matcher.group(2) != null ? Integer.valueOf(matcher.group(2)) - 1 : 0;
int day = matcher.group(3) != null ? Integer.valueOf(matcher.group(3)) : 0;

int h = (matcher.group(4) != null ? Integer.valueOf(matcher.group(4)) : 0);
int m = (matcher.group(5) != null ? Integer.valueOf(matcher.group(5)) : 0);
int s = (matcher.group(6) != null ? Integer.valueOf(matcher.group(6)) : 0);
int ms = Math.round(Float.parseFloat("0." + (matcher.group(7) != null ? matcher.group(7) : "0")) * 1000);

TimeZone timeZone = TimeZone.getTimeZone("GMT" + (matcher.group(8) != null ? matcher.group(8) : ""));

Calendar calendar = GregorianCalendar.getInstance(timeZone);
calendar.set(Calendar.YEAR, year);
calendar.set(Calendar.MONTH, month);
calendar.set(Calendar.DAY_OF_MONTH, day);
calendar.set(Calendar.HOUR_OF_DAY, h);
calendar.set(Calendar.MINUTE, m);
calendar.set(Calendar.SECOND, s);
calendar.set(Calendar.MILLISECOND, ms);

return calendar;
}
else
throw new java.text.ParseException("Unparseable ISO8601 date format: " + iso8601String, 0);
}

/** Transform ISO8601 string to java.util.Date */
public Date toDate(final String iso8601String) throws ParseException {
Calendar calendar = toCalendar(iso8601String);
return calendar.getTime();
}
}
Loading