Skip to content

Commit

Permalink
Date in millis handled as a number (#550)
Browse files Browse the repository at this point in the history
Date formatted as milliseconds are now handled as a number

Signed-off-by: David Kral <[email protected]>
  • Loading branch information
Verdent authored Apr 27, 2022
1 parent 0f76d01 commit 5bc927b
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 17 deletions.
20 changes: 20 additions & 0 deletions src/main/java/org/eclipse/yasson/YassonConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ public class YassonConfig extends JsonbConfig {
*/
public static final String JSONB_CREATOR_PARAMETERS_REQUIRED = "yasson.jsonb-creator-parameters-required";

/**
* @see #withTimeInMillisAsAString(boolean)
*/
public static final String DATE_TIME_IN_MILLIS_AS_A_STRING = "yasson.time-in-millis-as-a-string";

/**
* Property used to specify behaviour on deserialization when JSON document contains properties
* which doesn't exist in the target class. Default value is 'false'.
Expand Down Expand Up @@ -143,4 +148,19 @@ public YassonConfig withJsonbParametersRequired(boolean value) {
return this;
}

/**
* It is required to handle time millisecond format as a number. See
* {@link jakarta.json.bind.annotation.JsonbDateFormat#TIME_IN_MILLIS}. It is possible to override this and force
* Yasson to handle it as a String, by using this method.
*
* @param value whether to treat dates formatted by {@link jakarta.json.bind.annotation.JsonbDateFormat#TIME_IN_MILLIS}
* as a String. Default value is {@code false}.
* @return This YassonConfig instance
* @since 3.0.0
*/
public YassonConfig withTimeInMillisAsAString(boolean value) {
setProperty(DATE_TIME_IN_MILLIS_AS_A_STRING, value);
return this;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ public class JsonbConfigProperties {
private final boolean strictIJson;
private final boolean zeroTimeDefaulting;
private final boolean requiredCreatorParameters;
private final boolean dateInMillisecondsAsString;
private final Map<Class<?>, Class<?>> userTypeMapping;
private final Class<?> defaultMapImplType;
private final JsonbSerializer<Object> nullSerializer;
Expand Down Expand Up @@ -98,6 +99,7 @@ public JsonbConfigProperties(JsonbConfig jsonbConfig) {
this.eagerInitClasses = initEagerInitClasses();
this.requiredCreatorParameters = initRequiredCreatorParameters();
this.forceMapArraySerializerForNullKeys = initForceMapArraySerializerForNullKeys();
this.dateInMillisecondsAsString = initDateInMillisecondsAsString();
}

private Class<? extends Map> initDefaultMapImplType() {
Expand Down Expand Up @@ -192,6 +194,13 @@ private boolean initRequiredCreatorParameters() {
return getConfigProperty(YassonConfig.CREATOR_PARAMETERS_REQUIRED, Boolean.class, false);
}

private boolean initDateInMillisecondsAsString() {
if (System.getProperty(YassonConfig.DATE_TIME_IN_MILLIS_AS_A_STRING) != null) {
return Boolean.parseBoolean(System.getProperty(YassonConfig.DATE_TIME_IN_MILLIS_AS_A_STRING));
}
return getConfigProperty(YassonConfig.DATE_TIME_IN_MILLIS_AS_A_STRING, Boolean.class, false);
}

@SuppressWarnings("unchecked")
private JsonbSerializer<Object> initNullSerializer() {
return jsonbConfig.getProperty(YassonConfig.NULL_ROOT_SERIALIZER)
Expand Down Expand Up @@ -387,4 +396,8 @@ public Set<Class<?>> getEagerInitClasses() {
public boolean isForceMapArraySerializerForNullKeys() {
return forceMapArraySerializerForNullKeys;
}

public boolean isDateInMillisecondsAsString() {
return dateInMillisecondsAsString;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import java.time.temporal.TemporalAccessor;
import java.util.Locale;
import java.util.Optional;
import java.util.function.BiConsumer;
import java.util.function.Function;

import jakarta.json.bind.annotation.JsonbDateFormat;
Expand All @@ -35,11 +36,20 @@ abstract class AbstractDateSerializer<T> extends TypeSerializer<T> {

static final ZoneId UTC = ZoneId.of("UTC");

private final Function<T, String> valueSerializer;
private final Function<T, String> toStringSerializer;
private final BiConsumer<T, JsonGenerator> valueWriter;

AbstractDateSerializer(TypeSerializerBuilder serializerBuilder) {
super(serializerBuilder);
valueSerializer = valueSerializer(serializerBuilder);
Customization customization = serializerBuilder.getCustomization();
JsonbConfigProperties properties = serializerBuilder.getJsonbContext().getConfigProperties();
final JsonbDateFormatter formatter = getJsonbDateFormatter(properties, customization);
toStringSerializer = valueSerializer(serializerBuilder);
if (JsonbDateFormat.TIME_IN_MILLIS.equals(formatter.getFormat()) && !properties.isDateInMillisecondsAsString()) {
valueWriter = (value, generator) -> generator.write(toInstant(value).toEpochMilli());
} else {
valueWriter = (value, generator) -> generator.write(toStringSerializer.apply(value));
}
}

private Function<T, String> valueSerializer(TypeSerializerBuilder serializerBuilder) {
Expand Down Expand Up @@ -134,11 +144,11 @@ protected DateTimeFormatter getZonedFormatter(DateTimeFormatter formatter) {

@Override
void serializeValue(T value, JsonGenerator generator, SerializationContextImpl context) {
generator.write(valueSerializer.apply(value));
valueWriter.accept(value, generator);
}

@Override
void serializeKey(T key, JsonGenerator generator, SerializationContextImpl context) {
generator.writeKey(valueSerializer.apply(key));
generator.writeKey(toStringSerializer.apply(key));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
import jakarta.json.bind.annotation.JsonbTypeDeserializer;
import jakarta.json.bind.config.PropertyVisibilityStrategy;
import org.eclipse.yasson.TestTypeToken;
import org.eclipse.yasson.YassonConfig;
import org.eclipse.yasson.defaultmapping.dates.model.CalendarPojo;
import org.eclipse.yasson.defaultmapping.dates.model.ClassLevelDateAnnotation;
import org.eclipse.yasson.defaultmapping.dates.model.DatePojo;
Expand Down Expand Up @@ -157,12 +158,14 @@ public void testSqlDateTimeZonesMillis() {
private void testSqlDateTimeZonesMillis(TimeZone tz, long expectedMs) {
final TimeZone originalTZ = TimeZone.getDefault();
TimeZone.setDefault(tz);
try {
Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withDateFormat(JsonbDateFormat.TIME_IN_MILLIS, Locale.getDefault()));
JsonbConfig jsonbConfig = new JsonbConfig().withDateFormat(JsonbDateFormat.TIME_IN_MILLIS, Locale.getDefault());
try (Jsonb jsonb = JsonbBuilder.create(jsonbConfig)) {
java.sql.Date d = java.sql.Date.valueOf("1966-11-04");
String json = jsonb.toJson(d);
assertEquals("\"" + expectedMs + "\"", json);
assertEquals("" + expectedMs, json);
assertEquals(d, jsonb.fromJson(json, java.sql.Date.class));
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
TimeZone.setDefault(originalTZ);
}
Expand Down Expand Up @@ -215,7 +218,7 @@ public void testDate() throws ParseException {

// marshal to ISO format
final String expected = "{\"defaultFormatted\":\"2015-03-04T00:00:00Z[UTC]\"," +
"\"millisFormatted\":\"" + parsedDate.getTime()+ "\"," +
"\"millisFormatted\":" + parsedDate.getTime()+ "," +
"\"customDate\":\"00:00:00 | 04-03-2015\"}";
assertEquals(expected, bindingJsonb.toJson(pojo));

Expand Down Expand Up @@ -264,7 +267,7 @@ public void testCalendar() {

// marshal to ISO_DATE
final String expected = "{\"defaultFormatted\":\"2015-04-03T11:11:10+02:00[Europe/Prague]\"," +
"\"millisFormatted\":\"1428052270000\"," +
"\"millisFormatted\":1428052270000," +
"\"customCalendar\":\"11:11:10 | 03-04-2015, +0200\"}";
assertEquals(expected, bindingJsonb.toJson(calendarPojo));

Expand Down Expand Up @@ -303,8 +306,8 @@ public void testCalendarWithNonDefaultTimeZone() {

// marshal to ISO_DATE
final String expected = "{\"defaultFormatted\":\"2015-04-03T10:10:10+02:00[" + zoneId + "]\"," +
"\"millisFormatted\":\"" + cal.getTimeInMillis() +
"\",\"customCalendar\":\"10:10:10 | 03-04-2015, +0200\"}";
"\"millisFormatted\":" + cal.getTimeInMillis() + "," +
"\"customCalendar\":\"10:10:10 | 03-04-2015, +0200\"}";
assertEquals(expected, bindingJsonb.toJson(calendarPojo));

// marshal to ISO_DATE_TIME
Expand Down Expand Up @@ -343,7 +346,7 @@ public void testMarshalInstant() {
InstantPojo instantPojo = new InstantPojo(instant);

final String expected = "{\"defaultFormatted\":\"2015-03-03T23:00:00Z\"," +
"\"millisFormatted\":\"1425423600000\"," +
"\"millisFormatted\":1425423600000," +
"\"instant\":\"23:00:00 | 03-03-2015\"}";
assertEquals(expected, bindingJsonb.toJson(instantPojo));

Expand All @@ -353,6 +356,23 @@ public void testMarshalInstant() {
assertEquals(instant, result.instant);
}

@Test
public void testDateFormattedAsMillisInString() {
Jsonb jsonb = JsonbBuilder.create(new YassonConfig().withTimeInMillisAsAString(true));
final Instant instant = Instant.parse("2015-03-03T23:00:00Z");
InstantPojo instantPojo = new InstantPojo(instant);

final String expected = "{\"defaultFormatted\":\"2015-03-03T23:00:00Z\"," +
"\"millisFormatted\":\"1425423600000\"," +
"\"instant\":\"23:00:00 | 03-03-2015\"}";
assertEquals(expected, jsonb.toJson(instantPojo));

InstantPojo result = jsonb.fromJson(expected, InstantPojo.class);
assertEquals(instant, result.defaultFormatted);
assertEquals(instant, result.millisFormatted);
assertEquals(instant, result.instant);
}

@Test
public void testMarshalDuration() {
assertEquals("{\"value\":\"PT5H4M\"}", bindingJsonb.toJson(new ScalarValueWrapper<>(Duration.ofHours(5).plusMinutes(4))));
Expand All @@ -374,7 +394,7 @@ public void testLocalDate() {
final long millis = localDate.atStartOfDay(ZoneId.of("Z")).toInstant().toEpochMilli();

final String expected = "{\"defaultFormatted\":\"2015-04-10\"," +
"\"millisFormatted\":\"" + millis + "\"," +
"\"millisFormatted\":" + millis + "," +
"\"customLocalDate\":\"10-04-2015\"}";
assertEquals(expected, bindingJsonb.toJson(pojo));

Expand Down Expand Up @@ -422,7 +442,7 @@ public void testLocalDateTime() {
final long millis = dateTime.atZone(ZoneId.of("Z")).toInstant().toEpochMilli();

final String expected = "{\"defaultFormatted\":\"2015-02-16T13:21:00\"," +
"\"millisFormatted\":\"" + millis + "\"," +
"\"millisFormatted\":" + millis + "," +
"\"customLocalDate\":\"16-02-2015--00:21:13\"}";
assertEquals(expected, bindingJsonb.toJson(pojo));

Expand Down Expand Up @@ -456,7 +476,7 @@ public void testDifferentConfigsLocalDateTime() {
assertEquals(expected, bindingJsonb.toJson(pojo));

final Jsonb jsonbCustom = JsonbBuilder.create(new JsonbConfig().withDateFormat(JsonbDateFormat.TIME_IN_MILLIS, Locale.FRENCH));
assertEquals("{\"value\":\"" + millis + "\"}", jsonbCustom.toJson(pojo));
assertEquals("{\"value\":" + millis + "}", jsonbCustom.toJson(pojo));

ScalarValueWrapper<LocalDateTime> result = bindingJsonb.fromJson(expected, new TestTypeToken<ScalarValueWrapper<LocalDateTime>>(){}.getType());
assertEquals(dateTime, result.getValue());
Expand All @@ -472,7 +492,7 @@ public void testZonedDateTime() {
final ZonedDateTimePojo pojo = new ZonedDateTimePojo(dateTime);

final String expected = "{\"defaultFormatted\":\"2015-02-16T13:21:00+06:00[" + zone + "]\"," +
"\"millisFormatted\":\"" + dateTime.toInstant().toEpochMilli() + "\"," +
"\"millisFormatted\":" + dateTime.toInstant().toEpochMilli() + "," +
"\"customZonedDate\":\"+06" + zone + " | 16-02-2015--00:21:13\"}";
assertEquals(expected, bindingJsonb.toJson(pojo));

Expand Down Expand Up @@ -502,7 +522,7 @@ public void testMarshalOffsetDateTime() {
final OffsetDateTimePojo pojo = new OffsetDateTimePojo(dateTime);

final String expected = "{\"defaultFormatted\":\"2015-02-16T13:21:00+05:00\"," +
"\"millisFormatted\":\"1424074860000\"," +
"\"millisFormatted\":1424074860000," +
"\"offsetDateTime\":\"+0500 16-02-2015--00:21:13\"}";
assertEquals(expected, bindingJsonb.toJson(pojo));

Expand Down
1 change: 1 addition & 0 deletions src/test/resources/test.policy
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@ grant {
permission "java.util.PropertyPermission" "*", "write";

permission "java.util.PropertyPermission" "jsonb.creator-parameters-required", "read";
permission "java.util.PropertyPermission" "yasson.time-in-millis-as-a-string", "read";
permission "java.util.PropertyPermission" "jakarta.json.provider", "read";
};

0 comments on commit 5bc927b

Please sign in to comment.