You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@johnzon.apache.org by rm...@apache.org on 2019/08/10 10:41:17 UTC
[johnzon] branch master updated: JOHNZON-230 JOHNZON-231
JOHNZON-232 JOHNZON-233 JOHNZON-234 date/number format inheritance from
class/package + JSON-B ijson support
This is an automated email from the ASF dual-hosted git repository.
rmannibucau pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/johnzon.git
The following commit(s) were added to refs/heads/master by this push:
new 394445e JOHNZON-230 JOHNZON-231 JOHNZON-232 JOHNZON-233 JOHNZON-234 date/number format inheritance from class/package + JSON-B ijson support
394445e is described below
commit 394445e113affb70f3d019ebef2afac3ac577da2
Author: Romain Manni-Bucau <rm...@apache.org>
AuthorDate: Sat Aug 10 12:41:05 2019 +0200
JOHNZON-230 JOHNZON-231 JOHNZON-232 JOHNZON-233 JOHNZON-234 date/number format inheritance from class/package + JSON-B ijson support
---
.../org/apache/johnzon/jsonb/JohnzonBuilder.java | 48 ++++--
.../org/apache/johnzon/jsonb/JohnzonJsonb.java | 32 +++-
.../org/apache/johnzon/jsonb/JsonbAccessMode.java | 52 +++++-
.../jsonb/converter/JsonbDateConverter.java | 68 +++++++-
.../serializer/JohnzonDeserializationContext.java | 6 +-
.../org/apache/johnzon/jsonb/DateFormatTest.java | 104 ++++++++++++
.../java/org/apache/johnzon/jsonb/IJsonTest.java | 122 ++++++++++++++
.../org/apache/johnzon/jsonb/NumberFormatTest.java | 53 ++++++
.../java/org/apache/johnzon/jsonb/OrderTest.java | 179 +++++++++++++++++++++
.../org/apache/johnzon/jsonb/model/Holder.java | 24 +++
.../model/packageformat/FormatFromClassModel.java | 25 +++
.../packageformat/FormatFromPackageModel.java} | 22 +--
.../model/packageformat/FormatOnClassModel.java} | 25 ++-
.../jsonb/model/packageformat/package-info.java} | 25 +--
.../johnzon/mapper/DynamicMappingGenerator.java | 3 +-
.../johnzon/mapper/access/FieldAccessMode.java | 4 +-
.../org/apache/johnzon/mapper/access/Meta.java | 67 ++++++--
.../johnzon/mapper/access/MethodAccessMode.java | 4 +-
18 files changed, 762 insertions(+), 101 deletions(-)
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 b06b29f..9dcbb59 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
@@ -139,6 +139,19 @@ public class JohnzonBuilder implements JsonbBuilder {
config = new JsonbConfig();
}
+ final boolean ijson = config.getProperty(JsonbConfig.STRICT_IJSON)
+ .map(Boolean.class::cast)
+ .filter(it -> it)
+ .map(it -> {
+ if (!config.getProperty(JsonbConfig.BINARY_DATA_STRATEGY).isPresent()) {
+ config.withBinaryDataStrategy(BinaryDataStrategy.BASE_64);
+ }
+ if (!config.getProperty(JsonbConfig.DATE_FORMAT).isPresent()) {
+ config.withDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'xxx", Locale.ROOT);
+ }
+ return it;
+ }).orElse(false);
+
if (config.getProperty(JsonbConfig.FORMATTING).map(Boolean.class::cast).orElse(false)) {
builder.setPretty(true);
}
@@ -222,9 +235,6 @@ public class JohnzonBuilder implements JsonbBuilder {
defaultConverters.put(new AdapterKey(args[0], args[1]), johnzonJsonbAdapter);
}));
- config.getProperty(JsonbConfig.STRICT_IJSON).map(Boolean.class::cast).ifPresent(ijson -> {
- // no-op: https://tools.ietf.org/html/rfc7493 the only MUST of the spec should be fine by default
- });
config.getProperty("johnzon.fail-on-unknown-properties")
.map(v -> Boolean.class.isInstance(v) ? Boolean.class.cast(v) : Boolean.parseBoolean(String.valueOf(v)))
.ifPresent(builder::setFailOnUnknownProperties);
@@ -316,7 +326,7 @@ public class JohnzonBuilder implements JsonbBuilder {
}
final Mapper mapper = builder.build();
- return useCdi ? new JohnzonJsonb(mapper) {
+ return useCdi ? new JohnzonJsonb(mapper, ijson) {
{
cdiIntegration.track(this);
}
@@ -331,7 +341,7 @@ public class JohnzonBuilder implements JsonbBuilder {
}
}
}
- } : new JohnzonJsonb(mapper);
+ } : new JohnzonJsonb(mapper, ijson);
}
@@ -441,14 +451,15 @@ public class JohnzonBuilder implements JsonbBuilder {
converters.put(new AdapterKey(Calendar.class, String.class), new ConverterAdapter<>(new Converter<Calendar>() {
@Override
public String toString(final Calendar instance) {
- return ZonedDateTime.ofInstant(instance.toInstant(), zoneIDUTC).toString();
+ return ZonedDateTime.ofInstant(instance.toInstant(), instance.getTimeZone().toZoneId()).toString();
}
@Override
public Calendar fromString(final String text) {
+ final ZonedDateTime zonedDateTime = ZonedDateTime.parse(text);
final Calendar calendar = Calendar.getInstance();
- calendar.setTimeZone(timeZoneUTC);
- calendar.setTimeInMillis(ZonedDateTime.parse(text).toInstant().toEpochMilli());
+ calendar.setTimeZone(TimeZone.getTimeZone(zonedDateTime.getZone()));
+ calendar.setTimeInMillis(zonedDateTime.toInstant().toEpochMilli());
return calendar;
}
}));
@@ -460,9 +471,10 @@ public class JohnzonBuilder implements JsonbBuilder {
@Override
public GregorianCalendar fromString(final String text) {
+ final ZonedDateTime zonedDateTime = ZonedDateTime.parse(text);
final GregorianCalendar calendar = new GregorianCalendar();
- calendar.setTimeZone(timeZoneUTC);
- calendar.setTimeInMillis(ZonedDateTime.parse(text).toInstant().toEpochMilli());
+ calendar.setTimeZone(TimeZone.getTimeZone(zonedDateTime.getZone()));
+ calendar.setTimeInMillis(zonedDateTime.toInstant().toEpochMilli());
return calendar;
}
}));
@@ -687,13 +699,15 @@ public class JohnzonBuilder implements JsonbBuilder {
@Override
public String toString(final Calendar instance) {
- return formatter.format(ZonedDateTime.ofInstant(instance.toInstant(), zoneIDUTC));
+ return formatter.format(ZonedDateTime.ofInstant(instance.toInstant(), instance.getTimeZone().toZoneId()));
}
@Override
public Calendar fromString(final String text) {
- Calendar instance = Calendar.getInstance();
- instance.setTime(Date.from(parseZonedDateTime(text, formatter, zoneIDUTC).toInstant()));
+ final ZonedDateTime zonedDateTime = parseZonedDateTime(text, formatter, zoneIDUTC);
+ final Calendar instance = Calendar.getInstance();
+ instance.setTimeZone(TimeZone.getTimeZone(zonedDateTime.getZone()));
+ instance.setTime(Date.from(zonedDateTime.toInstant()));
return instance;
}
}));
@@ -701,13 +715,15 @@ public class JohnzonBuilder implements JsonbBuilder {
@Override
public String toString(final GregorianCalendar instance) {
- return formatter.format(ZonedDateTime.ofInstant(instance.toInstant(), zoneIDUTC));
+ return formatter.format(ZonedDateTime.ofInstant(instance.toInstant(), instance.getTimeZone().toZoneId()));
}
@Override
public GregorianCalendar fromString(final String text) {
- Calendar instance = GregorianCalendar.getInstance();
- instance.setTime(Date.from(parseZonedDateTime(text, formatter, zoneIDUTC).toInstant()));
+ final ZonedDateTime zonedDateTime = parseZonedDateTime(text, formatter, zoneIDUTC);
+ final Calendar instance = GregorianCalendar.getInstance();
+ instance.setTimeZone(TimeZone.getTimeZone(zonedDateTime.getZone()));
+ instance.setTime(Date.from(zonedDateTime.toInstant()));
return GregorianCalendar.class.cast(instance);
}
}));
diff --git a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnzonJsonb.java b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnzonJsonb.java
index fc44eef..62a055a 100644
--- a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnzonJsonb.java
+++ b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnzonJsonb.java
@@ -51,9 +51,11 @@ import java.util.OptionalLong;
// TODO: Optional handling for lists (and arrays)?
public class JohnzonJsonb implements Jsonb, AutoCloseable, JsonbExtension {
private final Mapper delegate;
+ private final boolean ijson;
- public JohnzonJsonb(final Mapper build) {
+ public JohnzonJsonb(final Mapper build, final boolean ijson) {
this.delegate = build;
+ this.ijson = ijson;
}
@Override
@@ -242,6 +244,8 @@ public class JohnzonJsonb implements Jsonb, AutoCloseable, JsonbExtension {
return delegate.writeArrayAsString(toArray(object));
} else if (Collection.class.isInstance(object)) {
return delegate.writeArrayAsString(Collection.class.cast(object));
+ } else if (ijson && isNotObjectOrArray(object)) {
+ throw new JsonbException("I-JSON mode only accepts arrays and objects as root instances");
}
return delegate.writeObjectAsString(object);
@@ -308,6 +312,8 @@ public class JohnzonJsonb implements Jsonb, AutoCloseable, JsonbExtension {
return delegate.writeArrayAsString((Object[]) object);
} else if (isCollection(runtimeType)) {
return delegate.writeArrayAsString(Collection.class.cast(object));
+ } else if (ijson && isNotObjectOrArray(object)) {
+ throw new JsonbException("I-JSON mode only accepts arrays and objects as root instances");
}
return delegate.writeObjectAsString(object);
}
@@ -324,6 +330,8 @@ public class JohnzonJsonb implements Jsonb, AutoCloseable, JsonbExtension {
delegate.writeArray((Object[]) object, writer);
} else if (Collection.class.isInstance(object)) {
delegate.writeArray(Collection.class.cast(object), writer);
+ } else if (ijson && isNotObjectOrArray(object)) {
+ throw new JsonbException("I-JSON mode only accepts arrays and objects as root instances");
} else {
delegate.writeObject(object, writer);
}
@@ -341,6 +349,8 @@ public class JohnzonJsonb implements Jsonb, AutoCloseable, JsonbExtension {
delegate.writeArray((Object[]) object, writer);
} else if (isCollection(runtimeType)) {
delegate.writeArray(Collection.class.cast(object), writer);
+ } else if (ijson && isNotObjectOrArray(object)) {
+ throw new JsonbException("I-JSON mode only accepts arrays and objects as root instances");
} else {
delegate.writeObject(object, writer);
}
@@ -353,6 +363,8 @@ public class JohnzonJsonb implements Jsonb, AutoCloseable, JsonbExtension {
delegate.writeArray((Object[]) object, stream);
} else if (Collection.class.isInstance(object)) {
delegate.writeArray(Collection.class.cast(object), stream);
+ } else if (ijson && isNotObjectOrArray(object)) {
+ throw new JsonbException("I-JSON mode only accepts arrays and objects as root instances");
} else {
delegate.writeObject(object, stream);
}
@@ -365,11 +377,29 @@ public class JohnzonJsonb implements Jsonb, AutoCloseable, JsonbExtension {
delegate.writeArray((Object[]) object, stream);
} else if (isCollection(runtimeType)) {
delegate.writeArray(Collection.class.cast(object), stream);
+ } else if (ijson && isNotObjectOrArray(object)) {
+ throw new JsonbException("I-JSON mode only accepts arrays and objects as root instances");
} else {
delegate.writeObject(object, stream);
}
}
+ private boolean isNotObjectOrArray(final Object object) {
+ if (String.class.isInstance(object) || Number.class.isInstance(object) || Boolean.class.isInstance(object)) {
+ return true;
+ }
+ if (JsonValue.class.isInstance(object)) {
+ switch (JsonValue.class.cast(object).getValueType()) {
+ case ARRAY:
+ case OBJECT:
+ return false;
+ default:
+ return true;
+ }
+ }
+ return false;
+ }
+
private Object unwrapOptional(final Object inObject) {
if (Optional.class.isInstance(inObject)) {
return Optional.class.cast(inObject).orElse(null);
diff --git a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JsonbAccessMode.java b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JsonbAccessMode.java
index bf34b52..2a396e3 100644
--- a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JsonbAccessMode.java
+++ b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JsonbAccessMode.java
@@ -38,6 +38,7 @@ import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZonedDateTime;
import java.util.ArrayList;
+import java.util.Calendar;
import java.util.Collection;
import java.util.Comparator;
import java.util.Date;
@@ -368,8 +369,8 @@ public class JsonbAccessMode implements AccessMode, Closeable {
converter = new ConverterAdapter<>(new JsonbLocalDateConverter(dateFormat));
} else if (ZonedDateTime.class == type) {
converter = new ConverterAdapter<>(new JsonbZonedDateTimeConverter(dateFormat));
- } else {
- throw new IllegalArgumentException(type + " not a supported date type");
+ } else { // can happen if set on the class, todo: refine the checks
+ converter = null; // todo: should we fallback on numberformat?
}
} else if (numberFormat != null) { // TODO: support lists?
converter = new ConverterAdapter<>(new JsonbNumberConverter(numberFormat));
@@ -611,7 +612,10 @@ public class JsonbAccessMode implements AccessMode, Closeable {
return Stream.of(genericInterfaces).filter(ParameterizedType.class::isInstance)
.filter(i -> Adapter.class.isAssignableFrom(Class.class.cast(ParameterizedType.class.cast(i).getRawType())))
.findFirst()
- .map(pt -> payloadType.isAssignableFrom(Class.class.cast(ParameterizedType.class.cast(pt).getActualTypeArguments()[0])))
+ .map(pt -> {
+ final Type argument = ParameterizedType.class.cast(pt).getActualTypeArguments()[0];
+ return Class.class.isInstance(argument) && payloadType.isAssignableFrom(Class.class.cast(argument));
+ })
.orElseGet(() -> {
final Class<?> superclass = aClass.getSuperclass();
return superclass != Object.class && isReversedAdapter(payloadType, superclass, instance);
@@ -666,6 +670,9 @@ public class JsonbAccessMode implements AccessMode, Closeable {
}
}
keyComparator = (o1, o2) -> {
+ if (o1 != null && o1.equals(o2)) {
+ return 0;
+ }
final int i1 = indexed.indexOf(o1);
final int i2 = indexed.indexOf(o2);
if (i1 < 0) {
@@ -684,6 +691,9 @@ public class JsonbAccessMode implements AccessMode, Closeable {
}
return 1;
}
+ if (i2 < 0) {
+ return -1;
+ }
return i1 - i2;
};
} else if (order != null) {
@@ -731,10 +741,16 @@ public class JsonbAccessMode implements AccessMode, Closeable {
ReaderConverters(final DecoratedType annotationHolder) {
final JsonbTypeDeserializer deserializer = annotationHolder.getAnnotation(JsonbTypeDeserializer.class);
final JsonbTypeAdapter adapter = annotationHolder.getAnnotation(JsonbTypeAdapter.class);
- final JsonbDateFormat dateFormat = annotationHolder.getAnnotation(JsonbDateFormat.class);
- final JsonbNumberFormat numberFormat = annotationHolder.getAnnotation(JsonbNumberFormat.class);
+ JsonbDateFormat dateFormat = annotationHolder.getAnnotation(JsonbDateFormat.class);
+ JsonbNumberFormat numberFormat = annotationHolder.getAnnotation(JsonbNumberFormat.class);
final JohnzonConverter johnzonConverter = annotationHolder.getAnnotation(JohnzonConverter.class);
validateAnnotations(annotationHolder, adapter, dateFormat, numberFormat, johnzonConverter);
+ if (dateFormat == null && isDateType(annotationHolder.getType())) {
+ dateFormat = annotationHolder.getClassOrPackageAnnotation(JsonbDateFormat.class);
+ }
+ if (numberFormat == null && isNumberType(annotationHolder.getType())) {
+ numberFormat = annotationHolder.getClassOrPackageAnnotation(JsonbNumberFormat.class);
+ }
converter = adapter == null && dateFormat == null && numberFormat == null && johnzonConverter == null ?
defaultConverters.get(new AdapterKey(annotationHolder.getType(), String.class)) :
@@ -814,10 +830,16 @@ public class JsonbAccessMode implements AccessMode, Closeable {
WriterConverters(final DecoratedType initialReader, final Types types) {
final JsonbTypeSerializer serializer = initialReader.getAnnotation(JsonbTypeSerializer.class);
final JsonbTypeAdapter adapter = initialReader.getAnnotation(JsonbTypeAdapter.class);
- final JsonbDateFormat dateFormat = initialReader.getAnnotation(JsonbDateFormat.class);
- final JsonbNumberFormat numberFormat = initialReader.getAnnotation(JsonbNumberFormat.class);
+ JsonbDateFormat dateFormat = initialReader.getAnnotation(JsonbDateFormat.class);
+ JsonbNumberFormat numberFormat = initialReader.getAnnotation(JsonbNumberFormat.class);
final JohnzonConverter johnzonConverter = initialReader.getAnnotation(JohnzonConverter.class);
validateAnnotations(initialReader, adapter, dateFormat, numberFormat, johnzonConverter);
+ if (dateFormat == null && isDateType(initialReader.getType())) {
+ dateFormat = initialReader.getClassOrPackageAnnotation(JsonbDateFormat.class);
+ }
+ if (numberFormat == null && isNumberType(initialReader.getType())) {
+ numberFormat = initialReader.getClassOrPackageAnnotation(JsonbNumberFormat.class);
+ }
converter = adapter == null && dateFormat == null && numberFormat == null && johnzonConverter == null ?
defaultConverters.get(new AdapterKey(initialReader.getType(), String.class)) :
@@ -844,6 +866,22 @@ public class JsonbAccessMode implements AccessMode, Closeable {
}
}
+ private boolean isDateType(final Type type) {
+ if (!Class.class.isInstance(type)) {
+ return false;
+ }
+ final Class<?> clazz = Class.class.cast(type);
+ return type.getTypeName().startsWith("java.time.") || Date.class == type || Calendar.class.isAssignableFrom(clazz);
+ }
+
+ private boolean isNumberType(final Type type) {
+ if (!Class.class.isInstance(type)) {
+ return false;
+ }
+ final Class<?> clazz = Class.class.cast(type);
+ return Number.class.isAssignableFrom(clazz) || clazz.isPrimitive();
+ }
+
private static class ClassDecoratedType implements DecoratedType {
private final Class<?> annotations;
diff --git a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/converter/JsonbDateConverter.java b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/converter/JsonbDateConverter.java
index 813009c..e800495 100644
--- a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/converter/JsonbDateConverter.java
+++ b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/converter/JsonbDateConverter.java
@@ -18,24 +18,86 @@
*/
package org.apache.johnzon.jsonb.converter;
-import javax.json.bind.annotation.JsonbDateFormat;
+import java.time.DateTimeException;
import java.time.Instant;
import java.time.LocalDateTime;
+import java.time.ZoneId;
import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
import java.util.Date;
+import javax.json.bind.annotation.JsonbDateFormat;
+
public class JsonbDateConverter extends JsonbDateConverterBase<Date> {
+ private static final ZoneId UTC = ZoneId.of("UTC");
+
+ // TODO: cheap impl to avoid to rely on exceptions, better can be to parse format
+ private volatile boolean hasTimezone = true;
+
public JsonbDateConverter(final JsonbDateFormat dateFormat) {
super(dateFormat);
}
@Override
public String toString(final Date instance) {
- return formatter == null ? Long.toString(instance.getTime()) : formatter.format(LocalDateTime.ofInstant(Instant.ofEpochMilli(instance.getTime()), ZoneOffset.UTC));
+ return formatter == null ?
+ Long.toString(instance.getTime()) :
+ toStringWithFormatter(instance);
}
@Override
public Date fromString(final String text) {
- return formatter == null ? new Date(Long.parseLong(text)) : Date.from(LocalDateTime.parse(text, formatter).toInstant(ZoneOffset.UTC));
+ return formatter == null ?
+ new Date(Long.parseLong(text)) :
+ fromStringWithFormatter(text);
+ }
+
+ private Date fromStringWithFormatter(final String text) {
+ final boolean hasTimezone = this.hasTimezone;
+ try {
+ if (hasTimezone) {
+ return fromZonedDateTime(text);
+ }
+ return fromLocalDateTime(text);
+ } catch (final DateTimeException dte) {
+ this.hasTimezone = !hasTimezone;
+ if (hasTimezone) {
+ return fromLocalDateTime(text);
+ }
+ return fromZonedDateTime(text);
+ }
+ }
+
+ private String toStringWithFormatter(final Date instance) {
+ final boolean hasTimezone = this.hasTimezone;
+ final Instant instant = Instant.ofEpochMilli(instance.getTime());
+ try {
+ if (hasTimezone) {
+ return toStringFromZonedDateTime(instant);
+ }
+ return toStringFromLocalDateTime(instant);
+ } catch (final DateTimeException dte) {
+ this.hasTimezone = !hasTimezone;
+ if (hasTimezone) {
+ return toStringFromLocalDateTime(instant);
+ }
+ return toStringFromZonedDateTime(instant);
+ }
+ }
+
+ private Date fromLocalDateTime(final String text) {
+ return Date.from(LocalDateTime.parse(text, formatter).toInstant(ZoneOffset.UTC));
+ }
+
+ private Date fromZonedDateTime(final String text) {
+ return Date.from(ZonedDateTime.parse(text, formatter).toInstant());
+ }
+
+ private String toStringFromLocalDateTime(final Instant instant) {
+ return formatter.format(LocalDateTime.ofInstant(instant, UTC));
+ }
+
+ private String toStringFromZonedDateTime(final Instant instant) {
+ return formatter.format(ZonedDateTime.ofInstant(instant, UTC));
}
}
diff --git a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/serializer/JohnzonDeserializationContext.java b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/serializer/JohnzonDeserializationContext.java
index 65b1595..f64b034 100644
--- a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/serializer/JohnzonDeserializationContext.java
+++ b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/serializer/JohnzonDeserializationContext.java
@@ -30,7 +30,6 @@ import javax.json.stream.JsonParser;
import javax.json.stream.JsonParsingException;
import org.apache.johnzon.mapper.MappingParser;
-import org.apache.johnzon.mapper.jsonp.RewindableJsonParser;
public class JohnzonDeserializationContext implements DeserializationContext {
private final MappingParser runtime;
@@ -56,8 +55,8 @@ public class JohnzonDeserializationContext implements DeserializationContext {
}
private JsonValue read(final JsonParser parser) {
- final JsonParser.Event next = RewindableJsonParser.class.isInstance(parser) ?
- RewindableJsonParser.class.cast(parser).getLast() : parser.next();
+ final JsonParser.Event next = /*RewindableJsonParser.class.isInstance(parser) ?
+ RewindableJsonParser.class.cast(parser).getLast() : */ parser.next();
switch (next) {
case START_OBJECT:
final JsonObjectBuilder objectBuilder = builderFactory.createObjectBuilder();
@@ -67,6 +66,7 @@ public class JohnzonDeserializationContext implements DeserializationContext {
final JsonArrayBuilder arrayBuilder = builderFactory.createArrayBuilder();
parseArray(parser, arrayBuilder);
return arrayBuilder.build();
+ case KEY_NAME:
case VALUE_STRING:
return jsonp.createValue(parser.getString());
case VALUE_FALSE:
diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/DateFormatTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/DateFormatTest.java
new file mode 100644
index 0000000..14709ea
--- /dev/null
+++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/DateFormatTest.java
@@ -0,0 +1,104 @@
+/*
+ * 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 java.text.SimpleDateFormat;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Locale;
+
+import javax.json.bind.Jsonb;
+import javax.json.bind.JsonbBuilder;
+import javax.json.bind.JsonbConfig;
+import javax.json.bind.annotation.JsonbDateFormat;
+
+import org.apache.johnzon.jsonb.model.Holder;
+import org.apache.johnzon.jsonb.model.packageformat.FormatOnClassModel;
+import org.apache.johnzon.jsonb.test.JsonbRule;
+import org.junit.Rule;
+import org.junit.Test;
+
+public class DateFormatTest {
+ @Rule
+ public final JsonbRule jsonb = new JsonbRule();
+
+ @Test
+ public void dateFormatMethods() {
+ final Date instance = new Date(0);
+ {
+ final String json = jsonb.toJson(new DateHolder() {{ setInstance(instance); }});
+ final String expected = "{\"instance\":\"" +
+ DateTimeFormatter.ofPattern("E DD MMM yyyy HH:mm:ss z")
+ .withLocale(new Locale.Builder().setLanguage("it").build())
+ .format(ZonedDateTime.ofInstant(instance.toInstant(), ZoneId.of("UTC"))) + "\"}";
+ assertEquals(expected, json);
+ }
+ {
+ final Calendar c = Calendar.getInstance(Locale.GERMAN);
+ c.set(Calendar.YEAR, Integer.parseInt("19700001".substring(0, 4)));
+ c.set(Calendar.MONTH, Integer.parseInt("19700001".substring(4, 6)));
+ c.set(Calendar.DATE, Integer.parseInt("19700001".substring(6, 8)));
+ final String json = String.format("{\"instance\":\"%s 1970 01:00:00 MEZ\"}",
+ new SimpleDateFormat("EEE' 'dd' 'MMM", Locale.GERMAN).format(c.getTime()));
+ final DateHolder holder = jsonb.fromJson(json, DateHolder.class);
+ assertEquals(instance, holder.getInstance());
+ }
+ }
+
+ @Test
+ public void packageConfigOverridenByClass() throws Exception {
+ try (final Jsonb jsonb = JsonbBuilder.create(new JsonbConfig()
+ .withDateFormat("E DD MMM yyyy HH:mm:ss z", Locale.CANADA))) {
+
+ final Date instance = new Date(0);
+ final Locale locale = new Locale.Builder().setLanguage("de").build();
+
+ {
+ final String json = jsonb.toJson(new FormatOnClassModel() {{ setInstance(instance); }});
+ final String expected = "{\"instance\":\""
+ + DateTimeFormatter.ofPattern("E DD MMM yyyy HH:mm:ss")
+ .withLocale(locale)
+ .format(ZonedDateTime.ofInstant(instance.toInstant(), ZoneId.of("UTC")))
+ + "\"}";
+ assertEquals(expected, json);
+ }
+ }
+ }
+
+ public static class DateHolder implements Holder<Date> {
+ private Date instance;
+
+ @Override
+ @JsonbDateFormat(value = "E DD MMM yyyy HH:mm:ss z", locale = "it")
+ public Date getInstance() {
+ return instance;
+ }
+
+ @Override
+ @JsonbDateFormat(value = "E DD MMM yyyy HH:mm:ss z", locale = "de")
+ public void setInstance(Date instance) {
+ this.instance = instance;
+ }
+ }
+}
diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/IJsonTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/IJsonTest.java
new file mode 100644
index 0000000..6d709fe
--- /dev/null
+++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/IJsonTest.java
@@ -0,0 +1,122 @@
+/*
+ * 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.fail;
+
+import java.util.Calendar;
+import java.util.Date;
+import java.util.TimeZone;
+
+import javax.json.bind.Jsonb;
+import javax.json.bind.JsonbBuilder;
+import javax.json.bind.JsonbConfig;
+import javax.json.bind.JsonbException;
+
+import org.apache.johnzon.jsonb.model.Holder;
+import org.junit.Test;
+
+public class IJsonTest {
+ @Test
+ public void binary() throws Exception {
+ final Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withStrictIJSON(true));
+ final String jsonString = jsonb.toJson(new Bytes());
+ assertEquals("{\"data\":\"VGVzdCBTdHJpbmc=\"}", jsonString);
+ jsonb.close();
+ }
+
+ @Test
+ public void date() throws Exception {
+ final Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withStrictIJSON(true));
+ final Calendar cal = Calendar.getInstance();
+ cal.clear();
+ cal.set(1970, 0, 1);
+ cal.setTimeZone(TimeZone.getTimeZone("UTC"));
+ final String json = jsonb.toJson(new DateHolder() {{ setInstance(cal.getTime()); }});
+ assertEquals("{\"instance\":\"1970-01-01T00:00:00Z+00:00\"}", json);
+ jsonb.close();
+ }
+
+ @Test
+ public void calendar() throws Exception {
+ final Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withStrictIJSON(true));
+
+ final Calendar cal = Calendar.getInstance();
+ cal.clear();
+ cal.set(1970, Calendar.JANUARY, 1);
+ cal.setTimeZone(TimeZone.getTimeZone("Europe/Paris"));
+
+ final String json = jsonb.toJson(new CalendarHolder() {{ setInstance(cal); }});
+ assertEquals("{\"instance\":\"1970-01-01T00:00:00Z+01:00\"}", json);
+ jsonb.close();
+ }
+
+ @Test
+ public void onlyObjectAndArrayCanBeRoot() throws Exception {
+ try (final Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withStrictIJSON(true))) {
+ try {
+ jsonb.toJson("Test String");
+ fail();
+ } catch (final JsonbException e) {
+ // ok
+ }
+ }
+ }
+
+ public class DateHolder implements Holder<Date> {
+ private Date instance;
+
+ @Override
+ public Date getInstance() {
+ return instance;
+ }
+
+ @Override
+ public void setInstance(final Date instance) {
+ this.instance = instance;
+ }
+ }
+
+ public class CalendarHolder implements Holder<Calendar> {
+ private Calendar instance;
+
+ @Override
+ public Calendar getInstance() {
+ return instance;
+ }
+
+ @Override
+ public void setInstance(final Calendar instance) {
+ this.instance = instance;
+ }
+ }
+
+ public static class Bytes {
+ private byte[] data = "Test String".getBytes();
+
+ public byte[] getData() {
+ return data;
+ }
+
+ public void setData(final byte[] data) {
+ this.data = data;
+ }
+ }
+}
diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/NumberFormatTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/NumberFormatTest.java
new file mode 100644
index 0000000..324aedb
--- /dev/null
+++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/NumberFormatTest.java
@@ -0,0 +1,53 @@
+/*
+ * 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.assertTrue;
+
+import org.apache.johnzon.jsonb.model.packageformat.FormatFromPackageModel;
+import org.apache.johnzon.jsonb.model.packageformat.FormatFromClassModel;
+import org.apache.johnzon.jsonb.test.JsonbRule;
+import org.junit.Rule;
+import org.junit.Test;
+
+public class NumberFormatTest {
+ @Rule
+ public final JsonbRule jsonb = new JsonbRule();
+
+ @Test
+ public void packageFormat() {
+ final String json = jsonb.toJson(new FormatFromPackageModel() {{ setInstance(123456.789); }});
+ assertTrue(json, json.matches("\\{\\s*\"instance\"\\s*:\\s*\"123.456,8\"\\s*}"));
+
+ final FormatFromPackageModel unmarshalledObject = jsonb.fromJson(
+ "{ \"instance\" : \"123.456,789\" }", FormatFromPackageModel.class);
+ assertEquals(123456.789, unmarshalledObject.getInstance(), 0);
+ }
+
+ @Test
+ public void formatType() {
+ final String json = jsonb.toJson(new FormatFromClassModel() {{ setInstance(123456.789); }});
+ assertTrue(json, json.matches("\\{\\s*\"instance\"\\s*:\\s*\"123.456,8\"\\s*}"));
+
+ final FormatFromPackageModel unmarshalledObject = jsonb.fromJson(
+ "{ \"instance\" : \"123.456,789\" }", FormatFromClassModel.class);
+ assertEquals(123456.789, unmarshalledObject.getInstance(), 0);
+ }
+}
diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/OrderTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/OrderTest.java
new file mode 100644
index 0000000..631a070
--- /dev/null
+++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/OrderTest.java
@@ -0,0 +1,179 @@
+/*
+ * 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.assertTrue;
+
+import java.lang.reflect.Type;
+
+import javax.json.bind.annotation.JsonbPropertyOrder;
+import javax.json.bind.annotation.JsonbTypeDeserializer;
+import javax.json.bind.annotation.JsonbTypeSerializer;
+import javax.json.bind.serializer.DeserializationContext;
+import javax.json.bind.serializer.JsonbDeserializer;
+import javax.json.bind.serializer.JsonbSerializer;
+import javax.json.bind.serializer.SerializationContext;
+import javax.json.stream.JsonGenerator;
+import javax.json.stream.JsonParser;
+
+import org.apache.johnzon.jsonb.model.Holder;
+import org.apache.johnzon.jsonb.test.JsonbRule;
+import org.junit.Rule;
+import org.junit.Test;
+
+public class OrderTest {
+ @Rule
+ public final JsonbRule jsonb = new JsonbRule();
+
+ @Test
+ public void partial() {
+ final String jsonb = this.jsonb.toJson(new PartialModel());
+ assertTrue(jsonb, jsonb.matches(
+ "\\{\\s*\"third\"\\s*:\\s*\"Third\"\\s*,\\s*\"fourth\"\\s*:\\s*\"Fourth\".*}"));
+ }
+
+ @Test
+ public void typeSerializer() {
+ final HolderHolder container = new HolderHolder();
+ final StringHolder instance = new StringHolder();
+ instance.setInstance("Test String");
+ container.setInstance(instance);
+
+ final String json = jsonb.toJson(container);
+ assertTrue(json, json.matches(
+ "\\{\\s*\"instance\"\\s*:\\s*\\{\\s*\"instance\"\\s*:\\s*\"Test String Serialized\"\\s*}\\s*}"));
+
+ final HolderHolder unmarshalledObject = jsonb.fromJson("{ \"instance\" : { \"instance\" : \"Test String\" } }", HolderHolder.class);
+ assertEquals("Test String Deserialized", unmarshalledObject.getInstance().getInstance());
+ }
+
+ public static class StringHolder implements Holder<String> {
+ private String instance = "Test";
+
+ public String getInstance() {
+ return instance;
+ }
+
+ public void setInstance(final String instance) {
+ this.instance = instance;
+ }
+ }
+
+ public static class SimpleContainerDeserializer implements JsonbDeserializer<StringHolder> {
+ @Override
+ public StringHolder deserialize(final JsonParser parser, final DeserializationContext ctx, final Type type) {
+ final StringHolder container = new StringHolder();
+
+ while (parser.hasNext()) {
+ final JsonParser.Event event = parser.next();
+ if (event == JsonParser.Event.START_OBJECT) {
+ continue;
+ }
+ if (event == JsonParser.Event.END_OBJECT) {
+ break;
+ }
+ if (event == JsonParser.Event.KEY_NAME && "instance".equals(parser.getString())) {
+ container.setInstance(ctx.deserialize(String.class, parser) + " Deserialized");
+ }
+ }
+
+ return container;
+ }
+ }
+
+ public static class SimpleContainerSerializer implements JsonbSerializer<StringHolder> {
+ @Override
+ public void serialize(final StringHolder container, final JsonGenerator generator,
+ final SerializationContext ctx) {
+ generator.writeStartObject();
+ ctx.serialize("instance", container.getInstance() + " Serialized", generator);
+ generator.writeEnd();
+ }
+ }
+
+ public static class HolderHolder implements Holder<StringHolder> {
+ @JsonbTypeSerializer(SimpleContainerSerializer.class)
+ @JsonbTypeDeserializer(SimpleContainerDeserializer.class)
+ private StringHolder instance;
+
+ @Override
+ public StringHolder getInstance() {
+ return instance;
+ }
+
+ @Override
+ public void setInstance(StringHolder instance) {
+ this.instance = instance;
+ }
+ }
+
+ @JsonbPropertyOrder({ "third", "fourth" })
+ public class PartialModel {
+ private String first = "First";
+
+ private String second = "Second";
+
+ private String third = "Third";
+
+ private String fourth = "Fourth";
+
+ private String anyOther = "Fifth String property starting with A";
+
+ public String getThird() {
+ return third;
+ }
+
+ public void setThird(final String third) {
+ this.third = third;
+ }
+
+ public String getFourth() {
+ return fourth;
+ }
+
+ public void setFourth(final String fourth) {
+ this.fourth = fourth;
+ }
+
+ public String getAnyOther() {
+ return anyOther;
+ }
+
+ public void setAnyOther(final String anyOther) {
+ this.anyOther = anyOther;
+ }
+
+ public String getFirst() {
+ return first;
+ }
+
+ public void setFirst(final String first) {
+ this.first = first;
+ }
+
+ public String getSecond() {
+ return second;
+ }
+
+ public void setSecond(final String second) {
+ this.second = second;
+ }
+ }
+}
diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/model/Holder.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/model/Holder.java
new file mode 100644
index 0000000..e0302de
--- /dev/null
+++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/model/Holder.java
@@ -0,0 +1,24 @@
+/*
+ * 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.model;
+
+public interface Holder<T> {
+ T getInstance();
+ void setInstance(T instance);
+}
diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/model/packageformat/FormatFromClassModel.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/model/packageformat/FormatFromClassModel.java
new file mode 100644
index 0000000..e6651e5
--- /dev/null
+++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/model/packageformat/FormatFromClassModel.java
@@ -0,0 +1,25 @@
+/*
+ * 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.model.packageformat;
+
+import javax.json.bind.annotation.JsonbNumberFormat;
+
+@JsonbNumberFormat("###,###.###")
+public class FormatFromClassModel extends FormatFromPackageModel {
+}
diff --git a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/converter/JsonbDateConverter.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/model/packageformat/FormatFromPackageModel.java
similarity index 51%
copy from johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/converter/JsonbDateConverter.java
copy to johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/model/packageformat/FormatFromPackageModel.java
index 813009c..0301c5c 100644
--- a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/converter/JsonbDateConverter.java
+++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/model/packageformat/FormatFromPackageModel.java
@@ -16,26 +16,20 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.johnzon.jsonb.converter;
+package org.apache.johnzon.jsonb.model.packageformat;
-import javax.json.bind.annotation.JsonbDateFormat;
-import java.time.Instant;
-import java.time.LocalDateTime;
-import java.time.ZoneOffset;
-import java.util.Date;
+import org.apache.johnzon.jsonb.model.Holder;
-public class JsonbDateConverter extends JsonbDateConverterBase<Date> {
- public JsonbDateConverter(final JsonbDateFormat dateFormat) {
- super(dateFormat);
- }
+public class FormatFromPackageModel implements Holder<Double> {
+ private Double instance;
@Override
- public String toString(final Date instance) {
- return formatter == null ? Long.toString(instance.getTime()) : formatter.format(LocalDateTime.ofInstant(Instant.ofEpochMilli(instance.getTime()), ZoneOffset.UTC));
+ public Double getInstance() {
+ return instance;
}
@Override
- public Date fromString(final String text) {
- return formatter == null ? new Date(Long.parseLong(text)) : Date.from(LocalDateTime.parse(text, formatter).toInstant(ZoneOffset.UTC));
+ public void setInstance(Double instance) {
+ this.instance = instance;
}
}
diff --git a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/converter/JsonbDateConverter.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/model/packageformat/FormatOnClassModel.java
similarity index 56%
copy from johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/converter/JsonbDateConverter.java
copy to johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/model/packageformat/FormatOnClassModel.java
index 813009c..24162f7 100644
--- a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/converter/JsonbDateConverter.java
+++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/model/packageformat/FormatOnClassModel.java
@@ -16,26 +16,25 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.johnzon.jsonb.converter;
+package org.apache.johnzon.jsonb.model.packageformat;
-import javax.json.bind.annotation.JsonbDateFormat;
-import java.time.Instant;
-import java.time.LocalDateTime;
-import java.time.ZoneOffset;
import java.util.Date;
-public class JsonbDateConverter extends JsonbDateConverterBase<Date> {
- public JsonbDateConverter(final JsonbDateFormat dateFormat) {
- super(dateFormat);
- }
+import javax.json.bind.annotation.JsonbDateFormat;
+
+import org.apache.johnzon.jsonb.model.Holder;
+
+@JsonbDateFormat(value = "E DD MMM yyyy HH:mm:ss", locale = "de")
+public class FormatOnClassModel implements Holder<Date> {
+ private Date instance;
@Override
- public String toString(final Date instance) {
- return formatter == null ? Long.toString(instance.getTime()) : formatter.format(LocalDateTime.ofInstant(Instant.ofEpochMilli(instance.getTime()), ZoneOffset.UTC));
+ public Date getInstance() {
+ return instance;
}
@Override
- public Date fromString(final String text) {
- return formatter == null ? new Date(Long.parseLong(text)) : Date.from(LocalDateTime.parse(text, formatter).toInstant(ZoneOffset.UTC));
+ public void setInstance(final Date instance) {
+ this.instance = instance;
}
}
diff --git a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/converter/JsonbDateConverter.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/model/packageformat/package-info.java
similarity index 52%
copy from johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/converter/JsonbDateConverter.java
copy to johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/model/packageformat/package-info.java
index 813009c..569da0e 100644
--- a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/converter/JsonbDateConverter.java
+++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/model/packageformat/package-info.java
@@ -16,26 +16,9 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.johnzon.jsonb.converter;
+@JsonbNumberFormat(value = "###,###.#", locale = "de")
+@JsonbDateFormat(value = "E DD MMM yyyy HH:mm:ss z", locale = "it")
+package org.apache.johnzon.jsonb.model.packageformat;
import javax.json.bind.annotation.JsonbDateFormat;
-import java.time.Instant;
-import java.time.LocalDateTime;
-import java.time.ZoneOffset;
-import java.util.Date;
-
-public class JsonbDateConverter extends JsonbDateConverterBase<Date> {
- public JsonbDateConverter(final JsonbDateFormat dateFormat) {
- super(dateFormat);
- }
-
- @Override
- public String toString(final Date instance) {
- return formatter == null ? Long.toString(instance.getTime()) : formatter.format(LocalDateTime.ofInstant(Instant.ofEpochMilli(instance.getTime()), ZoneOffset.UTC));
- }
-
- @Override
- public Date fromString(final String text) {
- return formatter == null ? new Date(Long.parseLong(text)) : Date.from(LocalDateTime.parse(text, formatter).toInstant(ZoneOffset.UTC));
- }
-}
+import javax.json.bind.annotation.JsonbNumberFormat;
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/DynamicMappingGenerator.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/DynamicMappingGenerator.java
index 86a80b6..5e7c5c3 100644
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/DynamicMappingGenerator.java
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/DynamicMappingGenerator.java
@@ -100,7 +100,8 @@ public class DynamicMappingGenerator implements MappingGenerator {
@Override
public JsonGenerator writeStartObject() {
- return delegate.writeStartObject();
+ // return delegate.writeStartObject();
+ return this;
}
@Override
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/FieldAccessMode.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/FieldAccessMode.java
index 831f895..95a9780 100644
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/FieldAccessMode.java
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/FieldAccessMode.java
@@ -109,9 +109,7 @@ public class FieldAccessMode extends BaseAccessMode {
@Override
public <T extends Annotation> T getClassOrPackageAnnotation(final Class<T> clazz) {
- final Class<?> declaringClass = field.getDeclaringClass();
- final T annotation = Meta.getAnnotation(declaringClass, clazz);
- return annotation == null ? Meta.getAnnotation(declaringClass.getPackage(), clazz) : annotation;
+ return Meta.getClassOrPackageAnnotation(field, clazz);
}
@Override
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/Meta.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/Meta.java
index 9e57c09..dbf8e00 100644
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/Meta.java
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/Meta.java
@@ -18,28 +18,66 @@
*/
package org.apache.johnzon.mapper.access;
+import static java.util.Arrays.asList;
+
import java.lang.annotation.Annotation;
import java.lang.reflect.AccessibleObject;
-import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
-
-import static java.util.Arrays.asList;
+import java.util.function.Supplier;
public final class Meta {
private Meta() {
// no-op
}
- public static <T extends Annotation> T getAnnotation(final AccessibleObject holder, final Class<T> api) {
+ public static <T extends Annotation> T getAnnotation(final Method holder, final Class<T> api) {
+ return getDirectAnnotation(holder, api);
+ }
+
+ public static <T extends Annotation> T getAnnotation(final Field holder, final Class<T> api) {
+ return getDirectAnnotation(holder, api);
+ }
+
+ public static <T extends Annotation> T getClassOrPackageAnnotation(final Method holder, final Class<T> api) {
+ return getIndirectAnnotation(api, holder::getDeclaringClass, () -> holder.getDeclaringClass().getPackage());
+ }
+
+ public static <T extends Annotation> T getClassOrPackageAnnotation(final Field holder, final Class<T> api) {
+ return getIndirectAnnotation(api, holder::getDeclaringClass, () -> holder.getDeclaringClass().getPackage());
+ }
+
+ private static <T extends Annotation> T getDirectAnnotation(final AccessibleObject holder, final Class<T> api) {
final T annotation = holder.getAnnotation(api);
if (annotation != null) {
return annotation;
}
- return findMeta(holder.getAnnotations(), api);
+ final T meta = findMeta(holder.getAnnotations(), api);
+ if (meta != null) {
+ return meta;
+ }
+ return null;
+ }
+
+ private static <T extends Annotation> T getIndirectAnnotation(final Class<T> api,
+ final Supplier<Class<?>> ownerSupplier,
+ final Supplier<Package> packageSupplier) {
+ final T ownerAnnotation = ownerSupplier.get().getAnnotation(api);
+ if (ownerAnnotation != null) {
+ return ownerAnnotation;
+ } // todo: meta?
+ final Package pck = packageSupplier.get();
+ if (pck != null) {
+ final T pckAnnotation = pck.getAnnotation(api);
+ if (pckAnnotation != null) {
+ return pckAnnotation;
+ }
+ } // todo: meta?
+ return null;
}
public static <T extends Annotation> T getAnnotation(final Class<?> clazz, final Class<T> api) {
@@ -81,18 +119,15 @@ public final class Meta {
private static <T extends Annotation> T newAnnotation(final Map<String, Method> methodMapping, final Annotation user, final T johnzon) {
return (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class<?>[]{johnzon.annotationType()},
- new InvocationHandler() {
- @Override
- public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
- final Method m = methodMapping.get(method.getName());
- try {
- if (m.getDeclaringClass() == user.annotationType()) {
- return m.invoke(user, args);
- }
- return m.invoke(johnzon, args);
- } catch (final InvocationTargetException ite) {
- throw ite.getTargetException();
+ (proxy, method, args) -> {
+ final Method m = methodMapping.get(method.getName());
+ try {
+ if (m.getDeclaringClass() == user.annotationType()) {
+ return m.invoke(user, args);
}
+ return m.invoke(johnzon, args);
+ } catch (final InvocationTargetException ite) {
+ throw ite.getTargetException();
}
});
}
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/MethodAccessMode.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/MethodAccessMode.java
index b694bd2..959d69f 100644
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/MethodAccessMode.java
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/MethodAccessMode.java
@@ -116,9 +116,7 @@ public class MethodAccessMode extends BaseAccessMode {
@Override
public <T extends Annotation> T getClassOrPackageAnnotation(final Class<T> clazz) {
- final Class<?> declaringClass = method.getDeclaringClass();
- final T annotation = Meta.getAnnotation(declaringClass, clazz);
- return annotation == null ? Meta.getAnnotation(declaringClass.getPackage(), clazz) : annotation;
+ return Meta.getClassOrPackageAnnotation(method, clazz);
}
@Override