Skip to content

Commit

Permalink
Implement YassonConfig.withEagerParsing(Class<?>...)
Browse files Browse the repository at this point in the history
  • Loading branch information
aguibert committed Nov 26, 2019
1 parent e54da09 commit 3529db1
Show file tree
Hide file tree
Showing 14 changed files with 251 additions and 92 deletions.
73 changes: 64 additions & 9 deletions src/main/java/org/eclipse/yasson/YassonConfig.java
Original file line number Diff line number Diff line change
@@ -1,52 +1,107 @@
/*
* Copyright (c) 2019 IBM and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0,
* or the Eclipse Distribution License v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
package org.eclipse.yasson;

import java.util.Map;

import javax.json.bind.JsonbConfig;
import javax.json.bind.serializer.JsonbSerializer;

import static org.eclipse.yasson.YassonProperties.*;
/**
* Custom properties for configuring Yasson outside of the specification {@link javax.json.bind.JsonbConfig} scope.
*/
public class YassonConfig extends JsonbConfig {

/**
* @see #withFailOnUnknownProperties(boolean)
*/
public static final String FAIL_ON_UNKNOWN_PROPERTIES = "jsonb.fail-on-unknown-properties";

import java.util.Map;
/**
* @see #withUserTypeMapping(java.util.Map)
*/
public static final String USER_TYPE_MAPPING = "jsonb.user-type-mapping";

/**
* @see #withZeroTimeParseDefaulting(boolean)
*/
public static final String ZERO_TIME_PARSE_DEFAULTING = "jsonb.zero-time-defaulting";

public class YassonConfig extends JsonbConfig {
/**
* @see #withNullRootSerializer(javax.json.bind.serializer.JsonbSerializer)
*/
public static final String NULL_ROOT_SERIALIZER = "yasson.null-root-serializer";

/**
* @see #withEagerParsing(Class...)
*/
public static final String EAGER_PARSE_CLASSES = "yasson.eager-parse-classes";

/**
* @param Whether or not to fail if unknown properties are encountered
* Property used to specify behaviour on deserialization when JSON document contains properties
* which doesn't exist in the target class. Default value is 'false'.
* @param failOnUnknownProperties Whether or not to fail if unknown properties are encountered
* @return This YassonConfig instance
* @see YassonProperties#FAIL_ON_UNKNOWN_PROPERTIES
*/
public YassonConfig withFailOnUnknownProperties(boolean failOnUnknownProperties) {
setProperty(FAIL_ON_UNKNOWN_PROPERTIES, failOnUnknownProperties);
return this;
}

/**
* @param mapping A map of interface class -> implementation class mappings
* User type mapping for map interface to implementation classes.
* @param mapping A map of interface to implementation class mappings
* @return This YassonConfig instance
* @see YassonProperties#USER_TYPE_MAPPING
*/
public YassonConfig withUserTypeMapping(Map<Class<?>, Class<?>> mapping) {
setProperty(USER_TYPE_MAPPING, mapping);
return this;
}

/**
* <p>Makes parsing dates defaulting to zero hour, minute and second.
* This will made available to parse patterns like yyyy.MM.dd to
* {@link java.util.Date}, {@link java.util.Calendar}, {@link java.time.Instant} {@link java.time.LocalDate}
* or even {@link java.time.ZonedDateTime}.
* <p>If time zone is not set in the pattern then UTC time zone is used.
* So for example json value 2018.01.01 becomes 2018.01.01 00:00:00 UTC when parsed
* to instant {@link java.time.Instant} or {@link java.time.ZonedDateTime}.
* @param defaultZeroHour Whether or not to default parsing dates to the zero hour
* @return This YassonConfig instance
* @see YassonProperties#ZERO_TIME_PARSE_DEFAULTING
*/
public YassonConfig withZeroTimeParseDefaulting(boolean defaultZeroHour) {
setProperty(ZERO_TIME_PARSE_DEFAULTING, defaultZeroHour);
return this;
}

/**
* Serializer to use when object provided to {@link javax.json.bind.Jsonb#toJson(Object)} is {@code null} or an empty
* Optional. Must be instance of {@link javax.json.bind.serializer.JsonbSerializer}{@code <Object>}. Its obj value
* will be respective parameter.
* @param nullSerializer JsonbSerializer instance to use for serializing null root values
* @return This YassonConfig instance
* @see YassonProperties#NULL_ROOT_SERIALIZER
*/
public YassonConfig withNullRootSerializer(JsonbSerializer<?> nullSerializer) {
setProperty(NULL_ROOT_SERIALIZER, nullSerializer);
return this;
}

/**
* @param classes A list of classes to eagerly parse upon creation of the Jsonb instance used with this configuration.
* @return This YassonConfig instance
*/
public YassonConfig withEagerParsing(Class<?>... classes) {
setProperty(EAGER_PARSE_CLASSES, classes);
return this;
}

}
34 changes: 16 additions & 18 deletions src/main/java/org/eclipse/yasson/YassonProperties.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,39 +14,37 @@
package org.eclipse.yasson;

/**
* Custom properties for configuring Yasson outside of the specification {@link javax.json.bind.JsonbConfig} scope.
* @deprecated Use {@link YassonConfig} instead
*/
@Deprecated
public class YassonProperties {

private YassonProperties() {
throw new IllegalStateException("Util classes cannot be instantiated.");
}

/**
* Property used to specify behaviour on deserialization when JSON document contains properties
* which doesn't exist in the target class. Default value is 'true'.
* @deprecated
* @see YassonConfig#withFailOnUnknownProperties(boolean)
*/
public static final String FAIL_ON_UNKNOWN_PROPERTIES = "jsonb.fail-on-unknown-properties";
public static final String FAIL_ON_UNKNOWN_PROPERTIES = YassonConfig.FAIL_ON_UNKNOWN_PROPERTIES;

/**
* User type mapping for map interface to implementation classes.
* @deprecated
* @see YassonConfig#withUserTypeMapping(java.util.Map)
*/
public static final String USER_TYPE_MAPPING = "jsonb.user-type-mapping";
public static final String USER_TYPE_MAPPING = YassonConfig.USER_TYPE_MAPPING;

/**
* <p>Makes parsing dates defaulting to zero hour, minute and second.
* This will made available to parse patterns like yyyy.MM.dd to
* {@link java.util.Date}, {@link java.util.Calendar}, {@link java.time.Instant} {@link java.time.LocalDate}
* or even {@link java.time.ZonedDateTime}.
* <p>If time zone is not set in the pattern then UTC time zone is used.
* So for example json value 2018.01.01 becomes 2018.01.01 00:00:00 UTC when parsed
* to instant {@link java.time.Instant} or {@link java.time.ZonedDateTime}.
* @deprecated
* @see YassonConfig#withZeroTimeParseDefaulting(boolean)
*/
public static final String ZERO_TIME_PARSE_DEFAULTING = "jsonb.zero-time-defaulting";
public static final String ZERO_TIME_PARSE_DEFAULTING = YassonConfig.ZERO_TIME_PARSE_DEFAULTING;

/**
* Serializer to use when object provided to {@link javax.json.bind.Jsonb#toJson(Object)} is {@code null} or an empty
* Optional. Must be instance of {@link javax.json.bind.serializer.JsonbSerializer}{@code <Object>}. Its obj value
* will be respective parameter.
* @deprecated
* @see YassonConfig#withNullRootSerializer(javax.json.bind.serializer.JsonbSerializer)
*/
public static final String NULL_ROOT_SERIALIZER = "yasson.null-root-serializer";
public static final String NULL_ROOT_SERIALIZER = YassonConfig.NULL_ROOT_SERIALIZER;

}
7 changes: 7 additions & 0 deletions src/main/java/org/eclipse/yasson/internal/JsonBinding.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

import javax.json.JsonStructure;
import javax.json.bind.JsonbConfig;
Expand All @@ -46,6 +47,12 @@ public class JsonBinding implements YassonJsonb {

JsonBinding(JsonBindingBuilder builder) {
this.jsonbContext = new JsonbContext(builder.getConfig(), builder.getProvider().orElseGet(JsonProvider::provider));
Set<Class<?>> eagerInitClasses = this.jsonbContext.getConfigProperties().getEagerInitClasses();
for (Class<?> eagerInitClass : eagerInitClasses) {
// Eagerly initialize requested ClassModels and Serializers
jsonbContext.getMappingContext().getOrCreateClassModel(eagerInitClass);
new Marshaller(jsonbContext).getRootSerializer(eagerInitClass);
}
}

private <T> T deserialize(final Type type, final JsonParser parser, final Unmarshaller unmarshaller) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,15 @@
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.temporal.ChronoField;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.TreeMap;
import java.util.function.Consumer;

Expand All @@ -34,7 +37,7 @@
import javax.json.bind.config.PropertyVisibilityStrategy;
import javax.json.bind.serializer.JsonbSerializer;

import org.eclipse.yasson.YassonProperties;
import org.eclipse.yasson.YassonConfig;
import org.eclipse.yasson.internal.model.PropertyModel;
import org.eclipse.yasson.internal.model.ReverseTreeMap;
import org.eclipse.yasson.internal.model.customization.PropertyOrdering;
Expand Down Expand Up @@ -76,6 +79,8 @@ public class JsonbConfigProperties {
private final Class<?> defaultMapImplType;

private final JsonbSerializer<Object> nullSerializer;

private final Set<Class<?>> eagerInitClasses;

/**
* Creates new resolved JSONB config.
Expand All @@ -97,6 +102,7 @@ public JsonbConfigProperties(JsonbConfig jsonbConfig) {
this.zeroTimeDefaulting = initZeroTimeDefaultingForJavaTime();
this.defaultMapImplType = initDefaultMapImplType();
this.nullSerializer = initNullSerializer();
this.eagerInitClasses = initEagerInitClasses();
}

private Class<?> initDefaultMapImplType() {
Expand All @@ -115,19 +121,19 @@ private Class<?> initDefaultMapImplType() {
}

private boolean initZeroTimeDefaultingForJavaTime() {
return getBooleanConfigProperty(YassonProperties.ZERO_TIME_PARSE_DEFAULTING, false);
return getBooleanConfigProperty(YassonConfig.ZERO_TIME_PARSE_DEFAULTING, false);
}

@SuppressWarnings("unchecked")
private Map<Class<?>, Class<?>> initUserTypeMapping() {
Optional<Object> property = jsonbConfig.getProperty(YassonProperties.USER_TYPE_MAPPING);
Optional<Object> property = jsonbConfig.getProperty(YassonConfig.USER_TYPE_MAPPING);
if (!property.isPresent()) {
return Collections.emptyMap();
}
Object result = property.get();
if (!(result instanceof Map)) {
throw new JsonbException(Messages.getMessage(MessageKeys.JSONB_CONFIG_PROPERTY_INVALID_TYPE,
YassonProperties.USER_TYPE_MAPPING,
YassonConfig.USER_TYPE_MAPPING,
Map.class.getSimpleName()));
}
return (Map<Class<?>, Class<?>>) result;
Expand Down Expand Up @@ -229,11 +235,12 @@ private boolean initConfigNullable() {
}

private boolean initConfigFailOnUnknownProperties() {
return getBooleanConfigProperty(YassonProperties.FAIL_ON_UNKNOWN_PROPERTIES, false);
return getBooleanConfigProperty(YassonConfig.FAIL_ON_UNKNOWN_PROPERTIES, false);
}

@SuppressWarnings("unchecked")
private JsonbSerializer<Object> initNullSerializer() {
Optional<Object> property = jsonbConfig.getProperty(YassonProperties.NULL_ROOT_SERIALIZER);
Optional<Object> property = jsonbConfig.getProperty(YassonConfig.NULL_ROOT_SERIALIZER);
if (!property.isPresent()) {
return new NullSerializer();
}
Expand All @@ -244,6 +251,18 @@ private JsonbSerializer<Object> initNullSerializer() {
}
return (JsonbSerializer<Object>) nullSerializer;
}

private Set<Class<?>> initEagerInitClasses() {
Optional<Object> property = jsonbConfig.getProperty(YassonConfig.EAGER_PARSE_CLASSES);
if (!property.isPresent()) {
return Collections.emptySet();
}
Object eagerInitClasses = property.get();
if (!(eagerInitClasses instanceof Class<?>[])) {
throw new JsonbException("YassonConfig.EAGER_PARSE_CLASSES must be instance of Class<?>[]");
}
return new HashSet<Class<?>>(Arrays.asList((Class<?>[]) eagerInitClasses));
}

/**
* Gets nullable from {@link JsonbConfig}.
Expand Down Expand Up @@ -405,4 +424,8 @@ public Class<?> getDefaultMapImplType() {
public JsonbSerializer<Object> getNullSerializer() {
return nullSerializer;
}

public Set<Class<?>> getEagerInitClasses() {
return eagerInitClasses;
}
}
4 changes: 2 additions & 2 deletions src/main/java/org/eclipse/yasson/internal/Marshaller.java
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ public <T> void serializeRoot(T root, JsonGenerator generator) {
rootSerializer.serialize(root, generator, this);
}

private JsonbSerializer<?> getRootSerializer(Class<?> rootClazz) {
JsonbSerializer<?> getRootSerializer(Class<?> rootClazz) {
final ContainerSerializerProvider serializerProvider = getMappingContext().getSerializerProvider(rootClazz);
if (serializerProvider != null) {
return serializerProvider
Expand All @@ -163,5 +163,5 @@ private JsonbSerializer<?> getRootSerializer(Class<?> rootClazz) {
}
return serializerBuilder.build();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
import org.eclipse.yasson.internal.properties.Messages;

/**
* Abstract class for converting date objects from {@link java.time}.
* Abstract class for converting date objects from java.time.
*
* @param <T> date type
*/
Expand Down Expand Up @@ -110,7 +110,7 @@ protected DateTimeFormatter getZonedFormatter(DateTimeFormatter formatter) {
protected abstract T fromInstant(Instant instant);

/**
* Parse {@link java.time} date object with default formatter.
* Parse java.time date object with default formatter.
* Different default formatter for each date object type is used.
*
* @param jsonValue string value to parse from
Expand All @@ -120,7 +120,7 @@ protected DateTimeFormatter getZonedFormatter(DateTimeFormatter formatter) {
protected abstract T parseDefault(String jsonValue, Locale locale);

/**
* Parse {@link java.time} date object with provided formatter.
* Parse java.time date object with provided formatter.
*
* @param jsonValue string value to parse from
* @param formatter a formatter to use
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ protected TemporalAccessor toTemporalAccessor(T object) {
protected abstract Instant toInstant(T value);

/**
* Format with default formatter for a given {@link java.time} date object.
* Format with default formatter for a given java.time date object.
* Different default formatter for each date object type is used.
*
* @param value date object
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/yasson-messages.properties
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ noCdiEnvironment=CDI environment is not available.
iJsonEnabledSingleValue=Cannot marshal single value because I-Json is enabled!
dateTypeNotSupported=Date type not supported: {0}
errorParsingDate=Error parsing {1} from value: {0}. Check your @JsonbDateFormat has all time units for {1} type, \
or consider using org.eclipse.yasson.YassonProperties#ZERO_TIME_PARSE_DEFAULTING.
or consider using org.eclipse.yasson.YassonConfig#ZERO_TIME_PARSE_DEFAULTING.
noDefaultConstructor=Cannot create instance of a class: {0}, No default constructor found.
offsetDateTimeFromMillis=Parsing {0} from epoch millisecond, UTC zone offset will be used.
timeToEpochMillisError=Cannot convert {0} to/from epoch milliseconds.
Expand Down

0 comments on commit 3529db1

Please sign in to comment.