mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
#1684 - ENH: Support ISO date format for JSON marshalling on LocalDate, sql.Date etc
This commit is contained in:
@@ -5,6 +5,22 @@ package io.ebean.config;
|
||||
*/
|
||||
public abstract class JsonConfig {
|
||||
|
||||
/**
|
||||
* Defined the format used for Date types.
|
||||
*/
|
||||
public enum Date {
|
||||
|
||||
/**
|
||||
* Format as epoch millis.
|
||||
*/
|
||||
MILLIS,
|
||||
|
||||
/**
|
||||
* Format as ISO-8601 date format.
|
||||
*/
|
||||
ISO8601
|
||||
}
|
||||
|
||||
/**
|
||||
* Defined the format used for DateTime types.
|
||||
*/
|
||||
|
||||
@@ -171,6 +171,11 @@ public class ServerConfig {
|
||||
*/
|
||||
private JsonConfig.DateTime jsonDateTime = JsonConfig.DateTime.MILLIS;
|
||||
|
||||
/**
|
||||
* The JSON format used for Date types. Default to millis.
|
||||
*/
|
||||
private JsonConfig.Date jsonDate = JsonConfig.Date.MILLIS;
|
||||
|
||||
/**
|
||||
* For writing JSON specify if null values or empty collections should be exluded.
|
||||
* By default all values are included.
|
||||
@@ -683,6 +688,20 @@ public class ServerConfig {
|
||||
this.jsonDateTime = jsonDateTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the JSON format used for Date types.
|
||||
*/
|
||||
public JsonConfig.Date getJsonDate() {
|
||||
return jsonDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the JSON format to use for Date types.
|
||||
*/
|
||||
public void setJsonDate(JsonConfig.Date jsonDate) {
|
||||
this.jsonDate = jsonDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the JSON include mode used when writing JSON.
|
||||
*/
|
||||
@@ -2988,12 +3007,8 @@ public class ServerConfig {
|
||||
queryBatchSize = p.getInt("queryBatchSize", queryBatchSize);
|
||||
|
||||
jsonInclude = p.getEnum(JsonConfig.Include.class, "jsonInclude", jsonInclude);
|
||||
String jsonDateTimeFormat = p.get("jsonDateTime", null);
|
||||
if (jsonDateTimeFormat != null) {
|
||||
jsonDateTime = JsonConfig.DateTime.valueOf(jsonDateTimeFormat);
|
||||
} else {
|
||||
jsonDateTime = JsonConfig.DateTime.MILLIS;
|
||||
}
|
||||
jsonDateTime = p.getEnum(JsonConfig.DateTime.class, "jsonDateTime", jsonDateTime);
|
||||
jsonDate = p.getEnum(JsonConfig.Date.class, "jsonDate", jsonDate);
|
||||
|
||||
ddlGenerate = p.getBoolean("ddl.generate", ddlGenerate);
|
||||
ddlRun = p.getBoolean("ddl.run", ddlRun);
|
||||
|
||||
@@ -79,27 +79,24 @@ public class DefaultTypeFactory {
|
||||
/**
|
||||
* Create the default ScalarType for java.util.Date.
|
||||
*/
|
||||
public ScalarType<java.util.Date> createUtilDate(JsonConfig.DateTime mode) {
|
||||
public ScalarType<java.util.Date> createUtilDate(JsonConfig.DateTime mode, JsonConfig.Date jsonDate) {
|
||||
// by default map anonymous java.util.Date to java.sql.Timestamp.
|
||||
// String mapType =
|
||||
// properties.getProperty("type.mapping.java.util.Date","timestamp");
|
||||
int utilDateType = getTemporalMapType("timestamp");
|
||||
|
||||
return createUtilDate(mode, utilDateType);
|
||||
return createUtilDate(mode, jsonDate, utilDateType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a ScalarType for java.util.Date explicitly specifying the type to
|
||||
* map to.
|
||||
*/
|
||||
public ScalarType<java.util.Date> createUtilDate(JsonConfig.DateTime mode, int utilDateType) {
|
||||
public ScalarType<java.util.Date> createUtilDate(JsonConfig.DateTime jsonDateTime, JsonConfig.Date jsonDate, int utilDateType) {
|
||||
|
||||
switch (utilDateType) {
|
||||
case Types.DATE:
|
||||
return new ScalarTypeUtilDate.DateType();
|
||||
return new ScalarTypeUtilDate.DateType(jsonDate);
|
||||
|
||||
case Types.TIMESTAMP:
|
||||
return new ScalarTypeUtilDate.TimestampType(mode);
|
||||
return new ScalarTypeUtilDate.TimestampType(jsonDateTime);
|
||||
|
||||
default:
|
||||
throw new RuntimeException("Invalid type " + utilDateType);
|
||||
|
||||
@@ -130,8 +130,6 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
|
||||
private final ScalarType<?> timeType = new ScalarTypeTime();
|
||||
|
||||
private final ScalarType<?> dateType = new ScalarTypeDate();
|
||||
|
||||
private final ScalarType<?> urlType = new ScalarTypeURL();
|
||||
private final ScalarType<?> uriType = new ScalarTypeURI();
|
||||
private final ScalarType<?> localeType = new ScalarTypeLocale();
|
||||
@@ -143,6 +141,7 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
private final ScalarType<?> classType = new ScalarTypeClass();
|
||||
|
||||
private final JsonConfig.DateTime jsonDateTime;
|
||||
private final JsonConfig.Date jsonDate;
|
||||
|
||||
private final Object objectMapper;
|
||||
|
||||
@@ -189,6 +188,7 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
|
||||
this.java7Present = config.getClassLoadConfig().isJava7Present();
|
||||
this.jsonDateTime = config.getJsonDateTime();
|
||||
this.jsonDate = config.getJsonDate();
|
||||
this.typeMap = new ConcurrentHashMap<>();
|
||||
this.nativeMap = new ConcurrentHashMap<>();
|
||||
this.logicalMap = new ConcurrentHashMap<>();
|
||||
@@ -205,9 +205,9 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
|
||||
this.defaultEnumType = config.getDefaultEnumType();
|
||||
|
||||
initialiseStandard(jsonDateTime, config);
|
||||
initialiseJavaTimeTypes(jsonDateTime, config);
|
||||
initialiseJodaTypes(jsonDateTime, config);
|
||||
initialiseStandard(config);
|
||||
initialiseJavaTimeTypes(config);
|
||||
initialiseJodaTypes(config);
|
||||
initialiseJacksonTypes(config);
|
||||
|
||||
loadTypesFromProviders(config, objectMapper);
|
||||
@@ -522,7 +522,7 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
}
|
||||
// a util Date with jdbcType not matching server wide settings
|
||||
if (type.equals(java.util.Date.class)) {
|
||||
return extraTypeFactory.createUtilDate(jsonDateTime, jdbcType);
|
||||
return extraTypeFactory.createUtilDate(jsonDateTime, jsonDate, jdbcType);
|
||||
}
|
||||
// a Calendar with jdbcType not matching server wide settings
|
||||
if (type.equals(java.util.Calendar.class)) {
|
||||
@@ -874,7 +874,7 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
}
|
||||
}
|
||||
|
||||
private void initialiseJavaTimeTypes(JsonConfig.DateTime mode, ServerConfig config) {
|
||||
private void initialiseJavaTimeTypes(ServerConfig config) {
|
||||
|
||||
if (java7Present) {
|
||||
typeMap.put(java.nio.file.Path.class, new ScalarTypePath());
|
||||
@@ -883,16 +883,16 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
if (config.getClassLoadConfig().isJavaTimePresent()) {
|
||||
logger.debug("Registering java.time data types");
|
||||
addType(java.time.Period.class, new ScalarTypePeriod());
|
||||
addType(java.time.LocalDate.class, new ScalarTypeLocalDate());
|
||||
addType(java.time.LocalDateTime.class, new ScalarTypeLocalDateTime(mode));
|
||||
addType(OffsetDateTime.class, new ScalarTypeOffsetDateTime(mode));
|
||||
addType(ZonedDateTime.class, new ScalarTypeZonedDateTime(mode));
|
||||
addType(Instant.class, new ScalarTypeInstant(mode));
|
||||
addType(java.time.LocalDate.class, new ScalarTypeLocalDate(jsonDate));
|
||||
addType(java.time.LocalDateTime.class, new ScalarTypeLocalDateTime(jsonDateTime));
|
||||
addType(OffsetDateTime.class, new ScalarTypeOffsetDateTime(jsonDateTime));
|
||||
addType(ZonedDateTime.class, new ScalarTypeZonedDateTime(jsonDateTime));
|
||||
addType(Instant.class, new ScalarTypeInstant(jsonDateTime));
|
||||
|
||||
addType(DayOfWeek.class, new ScalarTypeDayOfWeek());
|
||||
addType(Month.class, new ScalarTypeMonth());
|
||||
addType(Year.class, new ScalarTypeYear());
|
||||
addType(YearMonth.class, new ScalarTypeYearMonthDate());
|
||||
addType(YearMonth.class, new ScalarTypeYearMonthDate(jsonDate));
|
||||
addType(MonthDay.class, new ScalarTypeMonthDay());
|
||||
addType(OffsetTime.class, new ScalarTypeOffsetTime());
|
||||
addType(ZoneId.class, new ScalarTypeZoneId());
|
||||
@@ -915,16 +915,16 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
* Detect if Joda classes are in the classpath and if so register the Joda data types.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
private void initialiseJodaTypes(JsonConfig.DateTime mode, ServerConfig config) {
|
||||
private void initialiseJodaTypes(ServerConfig config) {
|
||||
|
||||
// detect if Joda classes are in the classpath
|
||||
if (config.getClassLoadConfig().isJodaTimePresent()) {
|
||||
// Joda classes are in the classpath so register the types
|
||||
logger.debug("Registering Joda data types");
|
||||
addType(LocalDateTime.class, new ScalarTypeJodaLocalDateTime(mode));
|
||||
addType(DateTime.class, new ScalarTypeJodaDateTime(mode));
|
||||
addType(LocalDate.class, new ScalarTypeJodaLocalDate());
|
||||
addType(org.joda.time.DateMidnight.class, new ScalarTypeJodaDateMidnight());
|
||||
addType(LocalDateTime.class, new ScalarTypeJodaLocalDateTime(jsonDateTime));
|
||||
addType(DateTime.class, new ScalarTypeJodaDateTime(jsonDateTime));
|
||||
addType(LocalDate.class, new ScalarTypeJodaLocalDate(jsonDate));
|
||||
addType(org.joda.time.DateMidnight.class, new ScalarTypeJodaDateMidnight(jsonDate));
|
||||
addType(org.joda.time.Period.class, new ScalarTypeJodaPeriod());
|
||||
|
||||
String jodaLocalTimeMode = config.getJodaLocalTimeMode();
|
||||
@@ -944,7 +944,7 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
* Register all the standard types supported. This is the standard JDBC types
|
||||
* plus some other common types such as java.util.Date and java.util.Calendar.
|
||||
*/
|
||||
private void initialiseStandard(JsonConfig.DateTime mode, ServerConfig config) {
|
||||
private void initialiseStandard(ServerConfig config) {
|
||||
|
||||
DatabasePlatform databasePlatform = config.getDatabasePlatform();
|
||||
int platformClobType = databasePlatform.getClobDbType();
|
||||
@@ -952,10 +952,10 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
|
||||
nativeMap.put(DbPlatformType.HSTORE, hstoreType);
|
||||
|
||||
ScalarType<?> utilDateType = extraTypeFactory.createUtilDate(mode);
|
||||
ScalarType<?> utilDateType = extraTypeFactory.createUtilDate(jsonDateTime, jsonDate);
|
||||
addType(java.util.Date.class, utilDateType);
|
||||
|
||||
ScalarType<?> calType = extraTypeFactory.createCalendar(mode);
|
||||
ScalarType<?> calType = extraTypeFactory.createCalendar(jsonDateTime);
|
||||
addType(Calendar.class, calType);
|
||||
|
||||
ScalarType<?> mathBigIntType = extraTypeFactory.createMathBigInteger();
|
||||
@@ -1079,10 +1079,12 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
// Temporal types
|
||||
addType(Time.class, timeType);
|
||||
nativeMap.put(Types.TIME, timeType);
|
||||
|
||||
ScalarTypeDate dateType = new ScalarTypeDate(jsonDate);
|
||||
addType(Date.class, dateType);
|
||||
nativeMap.put(Types.DATE, dateType);
|
||||
|
||||
ScalarType<?> timestampType = new ScalarTypeTimestamp(mode);
|
||||
ScalarType<?> timestampType = new ScalarTypeTimestamp(jsonDateTime);
|
||||
addType(Timestamp.class, timestampType);
|
||||
nativeMap.put(Types.TIMESTAMP, timestampType);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
import io.ebeanservice.docstore.api.mapping.DocPropertyType;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonToken;
|
||||
import io.ebean.config.JsonConfig;
|
||||
import io.ebeanservice.docstore.api.mapping.DocPropertyType;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
@@ -17,8 +18,11 @@ import java.sql.Types;
|
||||
*/
|
||||
public abstract class ScalarTypeBaseDate<T> extends ScalarTypeBase<T> {
|
||||
|
||||
public ScalarTypeBaseDate(Class<T> type, boolean jdbcNative, int jdbcType) {
|
||||
protected final JsonConfig.Date mode;
|
||||
|
||||
public ScalarTypeBaseDate(JsonConfig.Date mode, Class<T> type, boolean jdbcNative, int jdbcType) {
|
||||
super(type, jdbcNative, jdbcType);
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -93,9 +97,20 @@ public abstract class ScalarTypeBaseDate<T> extends ScalarTypeBase<T> {
|
||||
|
||||
@Override
|
||||
public void jsonWrite(JsonGenerator writer, T value) throws IOException {
|
||||
writer.writeNumber(convertToMillis(value));
|
||||
switch(mode) {
|
||||
case ISO8601:
|
||||
writer.writeString(toIsoFormat(value));
|
||||
break;
|
||||
default:
|
||||
writer.writeNumber(convertToMillis(value));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the value to ISO8601 format.
|
||||
*/
|
||||
protected abstract String toIsoFormat(T value);
|
||||
|
||||
@Override
|
||||
public DocPropertyType getDocType() {
|
||||
return DocPropertyType.DATE;
|
||||
|
||||
@@ -19,7 +19,7 @@ import java.sql.Types;
|
||||
public abstract class ScalarTypeBaseDateTime<T> extends ScalarTypeBase<T> {
|
||||
|
||||
|
||||
protected final DateTimeJsonParser dateTimeParser = new DateTimeJsonParser();
|
||||
protected final UtilDateTimeParser dateTimeParser = new UtilDateTimeParser();
|
||||
|
||||
protected final JsonConfig.DateTime mode;
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
import io.ebean.config.JsonConfig;
|
||||
import io.ebeaninternal.server.core.BasicTypeConverter;
|
||||
|
||||
import java.sql.Date;
|
||||
@@ -11,8 +12,13 @@ import java.sql.Types;
|
||||
*/
|
||||
public class ScalarTypeDate extends ScalarTypeBaseDate<java.sql.Date> {
|
||||
|
||||
public ScalarTypeDate() {
|
||||
super(Date.class, true, Types.DATE);
|
||||
public ScalarTypeDate(JsonConfig.Date mode) {
|
||||
super(mode, Date.class, true, Types.DATE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String toIsoFormat(Date value) {
|
||||
return value.toLocalDate().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
import io.ebean.config.JsonConfig;
|
||||
import io.ebeaninternal.server.core.BasicTypeConverter;
|
||||
import org.joda.time.DateMidnight;
|
||||
|
||||
import java.sql.Date;
|
||||
import java.sql.Types;
|
||||
@@ -14,8 +16,13 @@ public class ScalarTypeJodaDateMidnight extends ScalarTypeBaseDate<org.joda.time
|
||||
/**
|
||||
* Instantiates a new scalar type joda date midnight.
|
||||
*/
|
||||
public ScalarTypeJodaDateMidnight() {
|
||||
super(org.joda.time.DateMidnight.class, false, Types.DATE);
|
||||
public ScalarTypeJodaDateMidnight(JsonConfig.Date mode) {
|
||||
super(mode, org.joda.time.DateMidnight.class, false, Types.DATE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String toIsoFormat(DateMidnight value) {
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
import io.ebean.config.JsonConfig;
|
||||
import io.ebeaninternal.server.core.BasicTypeConverter;
|
||||
import org.joda.time.LocalDate;
|
||||
|
||||
@@ -11,8 +12,13 @@ import java.sql.Types;
|
||||
*/
|
||||
public class ScalarTypeJodaLocalDate extends ScalarTypeBaseDate<LocalDate> {
|
||||
|
||||
public ScalarTypeJodaLocalDate() {
|
||||
super(LocalDate.class, false, Types.DATE);
|
||||
public ScalarTypeJodaLocalDate(JsonConfig.Date mode) {
|
||||
super(mode, LocalDate.class, false, Types.DATE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String toIsoFormat(LocalDate value) {
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
import io.ebean.config.JsonConfig;
|
||||
import io.ebeaninternal.server.core.BasicTypeConverter;
|
||||
|
||||
import java.sql.Date;
|
||||
@@ -14,8 +15,12 @@ import java.time.ZonedDateTime;
|
||||
*/
|
||||
public class ScalarTypeLocalDate extends ScalarTypeBaseDate<LocalDate> {
|
||||
|
||||
public ScalarTypeLocalDate() {
|
||||
super(LocalDate.class, false, Types.DATE);
|
||||
public ScalarTypeLocalDate(JsonConfig.Date mode) {
|
||||
super(mode, LocalDate.class, false, Types.DATE);
|
||||
}
|
||||
|
||||
protected String toIsoFormat(LocalDate value) {
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -55,5 +60,4 @@ public class ScalarTypeLocalDate extends ScalarTypeBaseDate<LocalDate> {
|
||||
return new Timestamp(systemTimeMillis).toLocalDateTime().toLocalDate();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -82,10 +82,13 @@ public class ScalarTypeUtilDate {
|
||||
|
||||
public static class DateType extends ScalarTypeBaseDate<java.util.Date> {
|
||||
|
||||
public DateType() {
|
||||
super(Date.class, false, Types.DATE);
|
||||
public DateType(JsonConfig.Date mode) {
|
||||
super(mode, Date.class, false, Types.DATE);
|
||||
}
|
||||
|
||||
protected String toIsoFormat(java.util.Date value) {
|
||||
return UtilDateParser.format(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long convertToMillis(java.util.Date value) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
import io.ebean.config.JsonConfig;
|
||||
import io.ebeaninternal.server.core.BasicTypeConverter;
|
||||
|
||||
import java.sql.Date;
|
||||
@@ -15,8 +16,12 @@ import java.time.ZonedDateTime;
|
||||
*/
|
||||
public class ScalarTypeYearMonthDate extends ScalarTypeBaseDate<YearMonth> {
|
||||
|
||||
public ScalarTypeYearMonthDate() {
|
||||
super(YearMonth.class, false, Types.DATE);
|
||||
public ScalarTypeYearMonthDate(JsonConfig.Date mode) {
|
||||
super(mode, YearMonth.class, false, Types.DATE);
|
||||
}
|
||||
|
||||
protected String toIsoFormat(YearMonth value) {
|
||||
return value.atDay(1).toString();
|
||||
}
|
||||
|
||||
protected LocalDate toLocalDate(YearMonth yearMonth) {
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
class UtilDateParser {
|
||||
|
||||
private static final SimpleDateFormat dateTimeProto = new SimpleDateFormat("yyyy-MM-dd");
|
||||
|
||||
private static SimpleDateFormat formatter() {
|
||||
return (SimpleDateFormat) dateTimeProto.clone();
|
||||
}
|
||||
|
||||
static Date parse(String jsonDateTime) {
|
||||
try {
|
||||
return formatter().parse(jsonDateTime);
|
||||
} catch (ParseException e) {
|
||||
throw new RuntimeException("Error parsing Date[" + jsonDateTime + "]", e);
|
||||
}
|
||||
}
|
||||
|
||||
static String format(Date value) {
|
||||
return formatter().format(value);
|
||||
}
|
||||
}
|
||||
+2
-3
@@ -6,15 +6,14 @@ import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
|
||||
public class DateTimeJsonParser {
|
||||
class UtilDateTimeParser {
|
||||
|
||||
private final SimpleDateFormat dateTimeProto20;
|
||||
private final SimpleDateFormat dateTimeProto22;
|
||||
private final SimpleDateFormat dateTimeProto23;
|
||||
private final SimpleDateFormat dateTimeProto24;
|
||||
|
||||
|
||||
public DateTimeJsonParser() {
|
||||
UtilDateTimeParser() {
|
||||
dateTimeProto20 = init("yyyy-MM-dd'T'HH:mm:ss'Z'");
|
||||
dateTimeProto22 = init("yyyy-MM-dd'T'HH:mm:ss.S'Z'");
|
||||
dateTimeProto23 = init("yyyy-MM-dd'T'HH:mm:ss.SS'Z'");
|
||||
Reference in New Issue
Block a user