blob: 04d43705b3d8b42883a20ae34d4071d3b79c96ee [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.johnzon.jsonb;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import java.io.StringReader;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.URI;
import java.net.URL;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.time.OffsetTime;
import java.time.Period;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.Calendar;
import java.util.Comparator;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.Objects;
import java.util.Optional;
import java.util.OptionalDouble;
import java.util.OptionalInt;
import java.util.OptionalLong;
import java.util.SimpleTimeZone;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
import jakarta.json.bind.Jsonb;
import jakarta.json.bind.JsonbConfig;
import jakarta.json.bind.annotation.JsonbCreator;
import jakarta.json.bind.annotation.JsonbProperty;
import jakarta.json.bind.spi.JsonbProvider;
import org.apache.cxf.common.util.StringUtils;
import org.junit.Test;
public class JsonbTypesTest {
@Test
public void readAndWrite() throws Exception {
final LocalDate localDate = LocalDate.of(2015, 1, 1);
final LocalTime localTime = LocalTime.of(1, 2, 3);
final LocalDateTime localDateTime = LocalDateTime.of(2015, 1, 1, 1, 1);
final String expected = "{\"bigDecimal\":\"1.5\",\"bigInteger\":\"1\"," +
"\"calendar\":\"2015-01-01T01:01:00Z[UTC]\",\"date\":\"2015-01-01T01:01:00Z[UTC]\"," +
"\"duration\":\"PT30S\",\"gregorianCalendar\":\"2015-01-01T01:01:00Z[UTC]\"," +
"\"instant\":\"2015-01-01T00:00:00Z\",\"localDate\":\"2015-01-01\"," +
"\"localDateTime\":\"2015-01-01T01:01\",\"localTime\":\"01:02:03\"," +
"\"offsetDateTime\":\"2015-01-01T01:01Z\",\"offsetTime\":\"01:01Z\"," +
"\"optionalDouble\":3.4,\"optionalInt\":1,\"optionalLong\":2,\"optionalString\":\"yes\"," +
"\"period\":\"P1M10D\",\"simpleTimeZone\":\"UTC\",\"timeZone\":\"UTC\"," +
"\"uri\":\"http://localhost:2222\",\"url\":\"http://localhost:1111\"," +
"\"zoneId\":\"UTC\",\"zoneOffset\":\"Z\"}";
final Jsonb jsonb = newJsonb();
final Types types = jsonb.fromJson(new StringReader(expected), Types.class);
assertEquals("http://localhost:1111", types.url.toExternalForm());
assertEquals("http://localhost:2222", types.uri.toASCIIString());
assertEquals(Optional.of("yes"), types.optionalString);
assertEquals(1, types.optionalInt.getAsInt());
assertEquals(2, types.optionalLong.getAsLong());
assertEquals(3.4, types.optionalDouble.getAsDouble(), 0.);
assertEquals(localDateTime.toInstant(ZoneOffset.UTC).toEpochMilli(), types.date.getTime());
assertEquals(localDateTime.toInstant(ZoneOffset.UTC).toEpochMilli(), types.calendar.getTime().getTime());
assertEquals(localDateTime, types.localDateTime);
assertEquals(localTime, types.localTime);
assertEquals(localDate, types.localDate);
assertEquals(OffsetDateTime.of(localDateTime, ZoneOffset.UTC), types.offsetDateTime);
assertEquals(OffsetTime.of(localDateTime.toLocalTime(), ZoneOffset.UTC), types.offsetTime);
assertEquals(TimeZone.getTimeZone("UTC"), types.timeZone);
assertEquals(ZoneId.of("UTC"), types.zoneId);
assertEquals(ZoneOffset.UTC, types.zoneOffset);
assertEquals("UTC", types.simpleTimeZone.getID());
assertEquals(0, types.simpleTimeZone.getRawOffset());
assertEquals(TimeUnit.DAYS.toMillis(localDate.toEpochDay()), types.instant.toEpochMilli());
assertEquals(Duration.of(30, ChronoUnit.SECONDS), types.duration);
assertEquals(Period.of(0, 1, 10), types.period);
assertEquals(BigInteger.valueOf(1), types.bigInteger);
assertEquals(BigDecimal.valueOf(1.5), types.bigDecimal);
assertEquals(expected, jsonb.toJson(types));
jsonb.close();
}
@Test
public void testReadAndWriteBigIntDecimalAsNumbers() throws Exception {
final String expected = "{\"bigDecimal\":1.5,\"bigInteger\":1}";
final Jsonb jsonb = newJsonb(
new JsonbConfig()
.setProperty("johnzon.use-biginteger-stringadapter", false)
.setProperty("johnzon.use-bigdecimal-stringadapter", false));
final Types types = jsonb.fromJson(new StringReader(expected), Types.class);
assertEquals(BigInteger.valueOf(1), types.bigInteger);
assertEquals(BigDecimal.valueOf(1.5), types.bigDecimal);
assertEquals(expected, jsonb.toJson(types));
jsonb.close();
}
@Test
public void readAndWriteWithDateFormats() throws Exception {
readAndWriteWithDateFormat(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ"), "yyyy-MM-dd'T'HH:mm:ss.SSSZ");
readAndWriteWithDateFormat(DateTimeFormatter.ofPattern("yyyyMMdd+HHmmssZ"), "yyyyMMdd+HHmmssZ");
readAndWriteWithDateFormat(DateTimeFormatter.ofPattern("yyyy-MM-dd"), "yyyy-MM-dd");
}
@Test
public void testOptionalViaJsonbCreatorFullJson() {
final Jsonb jsonb = newJsonb();
final String json = "{\"intOptional\":4711,\"stringOptional\":\"testVal\"}";
final OptionalTypes optionalTypes = jsonb.fromJson(json, OptionalTypes.class);
assertNotNull(optionalTypes);
assertEquals("testVal", optionalTypes.getOptionalString());
assertEquals(4711, optionalTypes.getOptionalInt());
}
@Test
public void testOptionalViaJsonbCreatorEmptyJson() {
final Jsonb jsonb = newJsonb();
final String json = "{ }";
final OptionalTypes optionalTypes = jsonb.fromJson(json, OptionalTypes.class);
assertNotNull(optionalTypes);
assertEquals(OptionalTypes.EMPTY, optionalTypes.getOptionalString());
assertEquals(-1, optionalTypes.getOptionalInt());
}
@Test
public void testOptionalViaJsonbCreatorPartialJson() {
final Jsonb jsonb = newJsonb();
final String json = "{\"intOptional\":4711}";
final OptionalTypes optionalTypes = jsonb.fromJson(json, OptionalTypes.class);
assertNotNull(optionalTypes);
assertEquals(OptionalTypes.EMPTY, optionalTypes.getOptionalString());
assertEquals(4711, optionalTypes.getOptionalInt());
}
private void readAndWriteWithDateFormat(DateTimeFormatter dateTimeFormatter, String dateFormat) throws Exception {
final LocalDate localDate = LocalDate.of(2015, 1, 1);
final LocalDateTime localDateTime = LocalDateTime.of(2015, 1, 1, 1, 1);
final ZonedDateTime zonedDateTime = ZonedDateTime.of(localDateTime, ZoneId.of("UTC"));
final String expected = "{" +
"\"calendar\":\"" + dateTimeFormatter.format(zonedDateTime) + "\"," +
"\"date\":\"" + dateTimeFormatter.format(ZonedDateTime.ofInstant(localDateTime.toInstant(ZoneOffset.UTC), ZoneId.of("UTC"))) + "\"," +
"\"gregorianCalendar\":\"" + dateTimeFormatter.format(zonedDateTime) + "\"," +
"\"instant\":\"" + dateTimeFormatter.format(ZonedDateTime.ofInstant(Instant.ofEpochMilli(TimeUnit.DAYS.toMillis(localDate.toEpochDay())), ZoneId.of("UTC"))) + "\"," +
"\"localDate\":\"" + dateTimeFormatter.format(ZonedDateTime.ofInstant(Instant.ofEpochMilli(TimeUnit.DAYS.toMillis(localDate.toEpochDay())), ZoneId.of("UTC"))) + "\"," +
"\"localDateTime\":\"" + dateTimeFormatter.format(ZonedDateTime.ofInstant(localDateTime.toInstant(ZoneOffset.UTC), ZoneId.of("UTC"))) + "\"," +
"\"offsetDateTime\":\"" + dateTimeFormatter.format(ZonedDateTime.ofInstant(OffsetDateTime.of(localDateTime, ZoneOffset.UTC).toInstant(), ZoneId.of("UTC"))) + "\"" +
"}";
final Jsonb jsonb = newJsonb(dateFormat);
final DateTypes types = jsonb.fromJson(new StringReader(expected), DateTypes.class);
assertEquals(localDate, types.localDate);
assertEquals(expected, jsonb.toJson(types));
jsonb.close();
}
private static Jsonb newJsonb() {
return newJsonb((String) null);
}
private static Jsonb newJsonb(String dateFormat) {
JsonbConfig jsonbConfig = new JsonbConfig();
if (!StringUtils.isEmpty(dateFormat)){
jsonbConfig.withDateFormat(dateFormat, Locale.getDefault());
}
return newJsonb(jsonbConfig.setProperty("johnzon.attributeOrder", new Comparator<String>() {
@Override
public int compare(final String o1, final String o2) {
return o1.compareTo(o2);
}
}));
}
private static Jsonb newJsonb(JsonbConfig jsonbConfig) {
return JsonbProvider.provider().create().withConfig(jsonbConfig).build();
}
public static class Types {
private URL url;
private URI uri;
private Optional<String> optionalString;
private OptionalInt optionalInt;
private OptionalLong optionalLong;
private OptionalDouble optionalDouble;
private Date date;
private Calendar calendar;
private GregorianCalendar gregorianCalendar;
private TimeZone timeZone;
private ZoneId zoneId;
private ZoneOffset zoneOffset;
private SimpleTimeZone simpleTimeZone;
private Instant instant;
private Duration duration;
private Period period;
private LocalDateTime localDateTime;
private LocalTime localTime;
private LocalDate localDate;
private OffsetDateTime offsetDateTime;
private OffsetTime offsetTime;
private BigInteger bigInteger;
private BigDecimal bigDecimal;
public LocalTime getLocalTime() {
return localTime;
}
public void setLocalTime(final LocalTime localTime) {
this.localTime = localTime;
}
public URL getUrl() {
return url;
}
public void setUrl(URL url) {
this.url = url;
}
public URI getUri() {
return uri;
}
public void setUri(URI uri) {
this.uri = uri;
}
public Optional<String> getOptionalString() {
return optionalString;
}
public void setOptionalString(Optional<String> optionalString) {
this.optionalString = optionalString;
}
public OptionalInt getOptionalInt() {
return optionalInt;
}
public void setOptionalInt(OptionalInt optionalInt) {
this.optionalInt = optionalInt;
}
public OptionalLong getOptionalLong() {
return optionalLong;
}
public void setOptionalLong(OptionalLong optionalLong) {
this.optionalLong = optionalLong;
}
public OptionalDouble getOptionalDouble() {
return optionalDouble;
}
public void setOptionalDouble(OptionalDouble optionalDouble) {
this.optionalDouble = optionalDouble;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public Calendar getCalendar() {
return calendar;
}
public void setCalendar(Calendar calendar) {
this.calendar = calendar;
}
public GregorianCalendar getGregorianCalendar() {
return gregorianCalendar;
}
public void setGregorianCalendar(GregorianCalendar gregorianCalendar) {
this.gregorianCalendar = gregorianCalendar;
}
public TimeZone getTimeZone() {
return timeZone;
}
public void setTimeZone(TimeZone timeZone) {
this.timeZone = timeZone;
}
public ZoneId getZoneId() {
return zoneId;
}
public void setZoneId(ZoneId zoneId) {
this.zoneId = zoneId;
}
public ZoneOffset getZoneOffset() {
return zoneOffset;
}
public void setZoneOffset(ZoneOffset zoneOffset) {
this.zoneOffset = zoneOffset;
}
public SimpleTimeZone getSimpleTimeZone() {
return simpleTimeZone;
}
public void setSimpleTimeZone(SimpleTimeZone simpleTimeZone) {
this.simpleTimeZone = simpleTimeZone;
}
public Instant getInstant() {
return instant;
}
public void setInstant(Instant instant) {
this.instant = instant;
}
public Duration getDuration() {
return duration;
}
public void setDuration(Duration duration) {
this.duration = duration;
}
public Period getPeriod() {
return period;
}
public void setPeriod(Period period) {
this.period = period;
}
public LocalDateTime getLocalDateTime() {
return localDateTime;
}
public void setLocalDateTime(LocalDateTime localDateTime) {
this.localDateTime = localDateTime;
}
public LocalDate getLocalDate() {
return localDate;
}
public void setLocalDate(LocalDate localDate) {
this.localDate = localDate;
}
public OffsetDateTime getOffsetDateTime() {
return offsetDateTime;
}
public void setOffsetDateTime(OffsetDateTime offsetDateTime) {
this.offsetDateTime = offsetDateTime;
}
public OffsetTime getOffsetTime() {
return offsetTime;
}
public void setOffsetTime(OffsetTime offsetTime) {
this.offsetTime = offsetTime;
}
public BigInteger getBigInteger() {
return bigInteger;
}
public void setBigInteger(BigInteger bigInteger) {
this.bigInteger = bigInteger;
}
public BigDecimal getBigDecimal() {
return bigDecimal;
}
public void setBigDecimal(BigDecimal bigDecimal) {
this.bigDecimal = bigDecimal;
}
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final Types types = Types.class.cast(o);
return Objects.equals(url, types.url) &&
Objects.equals(uri, types.uri) &&
Objects.equals(optionalString, types.optionalString) &&
Objects.equals(optionalInt, types.optionalInt) &&
Objects.equals(optionalLong, types.optionalLong) &&
Objects.equals(optionalDouble, types.optionalDouble) &&
Objects.equals(date, types.date) &&
Objects.equals(calendar, types.calendar) &&
Objects.equals(gregorianCalendar, types.gregorianCalendar) &&
Objects.equals(timeZone, types.timeZone) &&
Objects.equals(zoneId, types.zoneId) &&
Objects.equals(zoneOffset, types.zoneOffset) &&
Objects.equals(simpleTimeZone, types.simpleTimeZone) &&
Objects.equals(instant, types.instant) &&
Objects.equals(duration, types.duration) &&
Objects.equals(period, types.period) &&
Objects.equals(localDateTime, types.localDateTime) &&
Objects.equals(localDate, types.localDate) &&
Objects.equals(offsetDateTime, types.offsetDateTime) &&
Objects.equals(offsetTime, types.offsetTime) &&
Objects.equals(bigInteger, types.bigInteger) &&
Objects.equals(bigDecimal, types.bigDecimal);
}
@Override
public int hashCode() {
return Objects.hash(
url, uri, optionalString, optionalInt, optionalLong, optionalDouble, date,
calendar, gregorianCalendar, timeZone, zoneId, zoneOffset, simpleTimeZone, instant, duration,
period, localDateTime, localDate, offsetDateTime, offsetTime, bigInteger, bigDecimal);
}
}
public static class DateTypes {
private Date date;
private Calendar calendar;
private GregorianCalendar gregorianCalendar;
private Instant instant;
private LocalDateTime localDateTime;
private LocalDate localDate;
private OffsetDateTime offsetDateTime;
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public Calendar getCalendar() {
return calendar;
}
public void setCalendar(Calendar calendar) {
this.calendar = calendar;
}
public GregorianCalendar getGregorianCalendar() {
return gregorianCalendar;
}
public void setGregorianCalendar(GregorianCalendar gregorianCalendar) {
this.gregorianCalendar = gregorianCalendar;
}
public Instant getInstant() {
return instant;
}
public void setInstant(Instant instant) {
this.instant = instant;
}
public LocalDateTime getLocalDateTime() {
return localDateTime;
}
public void setLocalDateTime(LocalDateTime localDateTime) {
this.localDateTime = localDateTime;
}
public LocalDate getLocalDate() {
return localDate;
}
public void setLocalDate(LocalDate localDate) {
this.localDate = localDate;
}
public OffsetDateTime getOffsetDateTime() {
return offsetDateTime;
}
public void setOffsetDateTime(OffsetDateTime offsetDateTime) {
this.offsetDateTime = offsetDateTime;
}
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final Types types = Types.class.cast(o);
return
Objects.equals(date, types.date) &&
Objects.equals(calendar, types.calendar) &&
Objects.equals(gregorianCalendar, types.gregorianCalendar) &&
Objects.equals(instant, types.instant) &&
Objects.equals(localDateTime, types.localDateTime) &&
Objects.equals(localDate, types.localDate) &&
Objects.equals(offsetDateTime, types.offsetDateTime);
}
@Override
public int hashCode() {
return Objects.hash(
date, calendar, gregorianCalendar, instant, localDateTime, localDate, offsetDateTime);
}
}
public static class OptionalTypes {
public final static String EMPTY = " ";
private final String optionalString;
private final int optionalInt;
public OptionalTypes(String optionalString, int optionalInt) {
this.optionalString = optionalString;
this.optionalInt = optionalInt;
}
@JsonbCreator
public static OptionalTypes init(@JsonbProperty("stringOptional") Optional<String> stringOptional,
@JsonbProperty("intOptional") OptionalInt intOptional) {
return new OptionalTypes(
stringOptional.orElse(EMPTY),
intOptional.orElse(-1));
}
public String getOptionalString() {
return optionalString;
}
public int getOptionalInt() {
return optionalInt;
}
}
}