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

Date in millis handled as a number #550

Merged
merged 2 commits into from
Apr 27, 2022
Merged
Show file tree
Hide file tree
Changes from all 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
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";
};