diff --git a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnzonBuilder.java b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnzonBuilder.java index 3e76eea8..fc43ce48 100644 --- a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnzonBuilder.java +++ b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnzonBuilder.java @@ -233,6 +233,16 @@ public Jsonb build() { Integer.parseInt(it.toString())) .ifPresent(builder::setSnippetMaxLength); + or(config.getProperty("johnzon.use-biginteger-stringadapter"), + () -> Optional.ofNullable(System.getProperty("johnzon.use-biginteger-stringadapter"))) + .map(Object::toString).map(Boolean::parseBoolean) + .ifPresent(builder::setUseBigIntegerStringAdapter); + + or(config.getProperty("johnzon.use-bigdecimal-stringadapter"), + () -> Optional.ofNullable(System.getProperty("johnzon.use-bigdecimal-stringadapter"))) + .map(Object::toString).map(Boolean::parseBoolean) + .ifPresent(builder::setUseBigDecimalStringAdapter); + // user adapters final Types types = new Types(); @@ -455,6 +465,10 @@ private ClassLoader tccl() { return map; } + private static Optional or(Optional first, Supplier> second) { + return first.isPresent() ? first : second.get(); + } + private static abstract class Lazy implements Supplier { private final AtomicReference ref = new AtomicReference<>(); diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/JsonbBigDecimalTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/JsonbBigDecimalTest.java new file mode 100644 index 00000000..345bceb3 --- /dev/null +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/JsonbBigDecimalTest.java @@ -0,0 +1,70 @@ +/* + * 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 org.apache.johnzon.jsonb.test.JsonbRule; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; + +import javax.json.bind.annotation.JsonbProperty; +import java.math.BigDecimal; +import java.math.BigInteger; + +public class JsonbBigDecimalTest { + @Rule + public final JsonbRule rule = new JsonbRule() + .withProperty("johnzon.use-biginteger-stringadapter", "false") + .withProperty("johnzon.use-bigdecimal-stringadapter", "false") + ; + + private static class BigNumberWrapper { + @JsonbProperty("bd") + private BigDecimal bd; + + @JsonbProperty("bi") + private BigInteger bi; + + public BigDecimal getBd() { + return bd; + } + + public void setBd(BigDecimal bd) { + this.bd = bd; + } + + public BigInteger getBi() { + return bi; + } + + public void setBi(BigInteger bi) { + this.bi = bi; + } + } + + @Test + public void jsonValue() { + final BigNumberWrapper bnw = new BigNumberWrapper(); + bnw.setBd(new BigDecimal("0.000000733915")); + bnw.setBi(new BigInteger("9223372036854775808")); + + final String json = rule.toJson(bnw); + Assert.assertEquals("{\"bd\":7.33915E-7,\"bi\":9223372036854775808}", json); + } +} \ No newline at end of file diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/JsonbTypesTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/JsonbTypesTest.java index dcf77cd5..2a646494 100644 --- a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/JsonbTypesTest.java +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/JsonbTypesTest.java @@ -21,6 +21,8 @@ import static org.junit.Assert.assertEquals; import java.io.StringReader; +import java.math.BigDecimal; +import java.math.BigInteger; import java.net.URI; import java.net.URL; import java.time.Duration; @@ -63,7 +65,8 @@ 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 = "{\"calendar\":\"2015-01-01T01:01:00Z[UTC]\",\"date\":\"2015-01-01T01:01:00Z[UTC]\"," + + 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\"," + @@ -103,6 +106,23 @@ public void readAndWrite() throws Exception { 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"); @@ -134,7 +154,7 @@ private void readAndWriteWithDateFormat(DateTimeFormatter dateTimeFormatter, Str } private static Jsonb newJsonb() { - return newJsonb(null); + return newJsonb((String) null); } private static Jsonb newJsonb(String dateFormat) { @@ -142,12 +162,17 @@ private static Jsonb newJsonb(String dateFormat) { if (!StringUtils.isEmpty(dateFormat)){ jsonbConfig.withDateFormat(dateFormat, Locale.getDefault()); } - return JsonbProvider.provider().create().withConfig(jsonbConfig.setProperty("johnzon.attributeOrder", new Comparator() { + + return newJsonb(jsonbConfig.setProperty("johnzon.attributeOrder", new Comparator() { @Override public int compare(final String o1, final String o2) { return o1.compareTo(o2); } - })).build(); + })); + } + + private static Jsonb newJsonb(JsonbConfig jsonbConfig) { + return JsonbProvider.provider().create().withConfig(jsonbConfig).build(); } public static class Types { @@ -172,6 +197,8 @@ public static class Types { private LocalDate localDate; private OffsetDateTime offsetDateTime; private OffsetTime offsetTime; + private BigInteger bigInteger; + private BigDecimal bigDecimal; public LocalTime getLocalTime() { return localTime; @@ -341,6 +368,22 @@ public void setOffsetTime(OffsetTime offsetTime) { this.offsetTime = offsetTime; } + public BigDecimal getBigDecimal() { + return bigDecimal; + } + + public void setBigDecimal(BigDecimal bigDecimal) { + this.bigDecimal = bigDecimal; + } + + public BigInteger getBigInteger() { + return bigInteger; + } + + public void setBigInteger(BigInteger bigInteger) { + this.bigInteger = bigInteger; + } + @Override public boolean equals(final Object o) { if (this == o) { @@ -369,7 +412,9 @@ public boolean equals(final Object o) { Objects.equals(localDateTime, types.localDateTime) && Objects.equals(localDate, types.localDate) && Objects.equals(offsetDateTime, types.offsetDateTime) && - Objects.equals(offsetTime, types.offsetTime); + Objects.equals(offsetTime, types.offsetTime) && + Objects.equals(bigInteger, types.bigInteger) && + Objects.equals(bigDecimal, types.bigDecimal); } @Override @@ -377,7 +422,7 @@ 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); + period, localDateTime, localDate, offsetDateTime, offsetTime, bigInteger, bigDecimal); } } diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/test/JsonbRule.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/test/JsonbRule.java index f617fe1e..0f236b26 100644 --- a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/test/JsonbRule.java +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/test/JsonbRule.java @@ -62,6 +62,11 @@ public JsonbRule withTypeAdapter(JsonbAdapter... jsonbAdapters) { return this; } + public JsonbRule withProperty(final String propertyName, final Object value) { + config.setProperty(propertyName, value); + return this; + } + @Override public Statement apply(final Statement statement, final Description description) { return new Statement() { diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperBuilder.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperBuilder.java index 77dbeb44..21b77aba 100644 --- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperBuilder.java +++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperBuilder.java @@ -269,6 +269,16 @@ public MapperBuilder setAdaptersDateTimeFormatter(final DateTimeFormatter dateTi return this; } + public MapperBuilder setUseBigIntegerStringAdapter(final boolean convertBigIntegerToString) { + adapters.setUseBigIntegerStringAdapter(convertBigIntegerToString); + return this; + } + + public MapperBuilder setUseBigDecimalStringAdapter(final boolean convertBigDecimalToString) { + adapters.setUseBigDecimalStringAdapter(convertBigDecimalToString); + return this; + } + public MapperBuilder setAdaptersDateTimeFormatterString(final String dateTimeFormatter) { adapters.setDateTimeFormatter(DateTimeFormatter.ofPattern(dateTimeFormatter)); return this; @@ -529,16 +539,6 @@ public MapperBuilder setUseJsRange(boolean value) { return this; } - public MapperBuilder setUseBigIntegerStringAdapter(final boolean convertBigIntegerToString) { - adapters.setUseBigIntegerStringAdapter(convertBigIntegerToString); - return this; - } - - public MapperBuilder setUseBigDecimalStringAdapter(final boolean convertBigDecimalToString) { - adapters.setUseBigDecimalStringAdapter(convertBigDecimalToString); - return this; - } - public MapperBuilder setUseBigDecimalForObjectNumbers(final boolean value) { this.useBigDecimalForObjectNumbers = value; return this; diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/map/LazyConverterMap.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/map/LazyConverterMap.java index 823065b7..b0cee385 100644 --- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/map/LazyConverterMap.java +++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/map/LazyConverterMap.java @@ -86,8 +86,8 @@ public Object from(final Object a) { private boolean useShortISO8601Format = true; private DateTimeFormatter dateTimeFormatter; // I-JSON (RFC 7493 Section 2.2): BigX exceed IEEE 754 double, string is safer by default. - // Set -Djohnzon.use-ijson-big-number-stringadapter.disabled=true for strict JSON-B 3.0 / TCK compliance. - private static final boolean IJSON_BIG_NUMBER_DEFAULT = !Boolean.getBoolean("johnzon.use-ijson-big-number-stringadapter.disabled"); + // Set -Djohnzon.use-big-number-stringadapter=false for strict JSON-B 3.0 / TCK compliance. + private static final boolean IJSON_BIG_NUMBER_DEFAULT = !Boolean.getBoolean("johnzon.use-big-number-stringadapter.disabled"); private boolean useBigIntegerStringAdapter = IJSON_BIG_NUMBER_DEFAULT; private boolean useBigDecimalStringAdapter = IJSON_BIG_NUMBER_DEFAULT; @@ -99,11 +99,11 @@ public void setDateTimeFormatter(final DateTimeFormatter dateTimeFormatter) { this.dateTimeFormatter = dateTimeFormatter; } - public void setUseBigDecimalStringAdapter(final boolean useBigDecimalStringAdapter) { + public void setUseBigDecimalStringAdapter(boolean useBigDecimalStringAdapter) { this.useBigDecimalStringAdapter = useBigDecimalStringAdapter; } - public void setUseBigIntegerStringAdapter(final boolean useBigIntegerStringAdapter) { + public void setUseBigIntegerStringAdapter(boolean useBigIntegerStringAdapter) { this.useBigIntegerStringAdapter = useBigIntegerStringAdapter; } @@ -139,13 +139,13 @@ public void setUseBigIntegerStringAdapter(final boolean useBigIntegerStringAdapt public Set adapterKeys() { return Stream.concat( - super.keySet().stream() - .filter(it -> super.get(it) != NO_ADAPTER), - Stream.of(Date.class, URI.class, URL.class, Class.class, String.class, BigDecimal.class, BigInteger.class, - Locale.class, Period.class, Duration.class, Calendar.class, GregorianCalendar.class, TimeZone.class, - ZoneId.class, ZoneOffset.class, SimpleTimeZone.class, Instant.class, LocalDateTime.class, LocalDate.class, - ZonedDateTime.class, OffsetDateTime.class, OffsetTime.class) - .map(it -> new AdapterKey(it, String.class, true))) + super.keySet().stream() + .filter(it -> super.get(it) != NO_ADAPTER), + Stream.of(Date.class, URI.class, URL.class, Class.class, String.class, BigDecimal.class, BigInteger.class, + Locale.class, Period.class, Duration.class, Calendar.class, GregorianCalendar.class, TimeZone.class, + ZoneId.class, ZoneOffset.class, SimpleTimeZone.class, Instant.class, LocalDateTime.class, LocalDate.class, + ZonedDateTime.class, OffsetDateTime.class, OffsetTime.class) + .map(it -> new AdapterKey(it, String.class, true))) .collect(toSet()); } diff --git a/pom.xml b/pom.xml index e8442784..f3909a82 100644 --- a/pom.xml +++ b/pom.xml @@ -413,7 +413,7 @@ - +