Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cache property include/exclude/sorting for serialization + simplify any #626

Merged
merged 1 commit into from
Oct 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import io.micronaut.core.util.ArrayUtils;
import io.micronaut.core.util.CollectionUtils;
import io.micronaut.serde.Encoder;
import io.micronaut.serde.ObjectSerializer;
import io.micronaut.serde.Serializer;
import io.micronaut.serde.exceptions.SerdeException;
import io.micronaut.serde.util.CustomizableSerializer;
Expand All @@ -37,30 +38,36 @@
final class CustomizedMapSerializer<K, V> implements CustomizableSerializer<Map<K, V>> {

@Override
public Serializer<Map<K, V>> createSpecific(EncoderContext context, Argument<? extends Map<K, V>> type) throws SerdeException {
public ObjectSerializer<Map<K, V>> createSpecific(EncoderContext context, Argument<? extends Map<K, V>> type) throws SerdeException {
final Argument[] generics = type.getTypeParameters();
final boolean hasGenerics = ArrayUtils.isNotEmpty(generics) && generics.length != 2;
if (hasGenerics) {
final Argument<V> valueGeneric = (Argument<V>) generics[1];
final Serializer<V> valSerializer = (Serializer<V>) context.findSerializer(valueGeneric).createSpecific(context, valueGeneric);
return new Serializer<>() {
return new ObjectSerializer<>() {

@Override
public void serialize(Encoder encoder, EncoderContext context, Argument<? extends Map<K, V>> type, Map<K, V> value) throws IOException {
final Encoder childEncoder = encoder.encodeObject(type);
final Encoder objectEncoder = encoder.encodeObject(type);
serializeInto(objectEncoder, context, type, value);
objectEncoder.finishStructure();
}

@Override
public void serializeInto(Encoder encoder, EncoderContext context, Argument<? extends Map<K, V>> type, Map<K, V> value) throws IOException {
for (K k : value.keySet()) {
encodeMapKey(context, childEncoder, k);
encodeMapKey(context, encoder, k);
final V v = value.get(k);
if (v == null) {
childEncoder.encodeNull();
encoder.encodeNull();
} else {
valSerializer.serialize(
childEncoder,
context,
valueGeneric, v
encoder,
context,
valueGeneric, v
);
}
}
childEncoder.finishStructure();
}

@Override
Expand All @@ -69,28 +76,34 @@ public boolean isEmpty(EncoderContext context, Map<K, V> value) {
}
};
} else {
return new Serializer<>() {
return new ObjectSerializer<>() {

@Override
public void serialize(Encoder encoder, EncoderContext context, Argument<? extends Map<K, V>> type, Map<K, V> value) throws IOException {
// slow path, lookup each value serializer
final Encoder childEncoder = encoder.encodeObject(type);
serializeInto(encoder, context, type, value);
childEncoder.finishStructure();
}

@Override
public void serializeInto(Encoder encoder, EncoderContext context, Argument<? extends Map<K, V>> type, Map<K, V> value) throws IOException {
for (Map.Entry<K, V> entry : value.entrySet()) {
encodeMapKey(context, childEncoder, entry.getKey());
encodeMapKey(context, encoder, entry.getKey());
final V v = entry.getValue();
if (v == null) {
childEncoder.encodeNull();
encoder.encodeNull();
} else {
@SuppressWarnings("unchecked") final Argument<V> valueGeneric = (Argument<V>) Argument.of(v.getClass());
final Serializer<? super V> valSerializer = context.findSerializer(valueGeneric)
.createSpecific(context, valueGeneric);
valSerializer.serialize(
childEncoder,
context,
valueGeneric, v
encoder,
context,
valueGeneric, v
);
}
}
childEncoder.finishStructure();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
import io.micronaut.core.annotation.Internal;
import io.micronaut.core.beans.exceptions.IntrospectionException;
import io.micronaut.core.type.Argument;
import io.micronaut.core.util.CollectionUtils;
import io.micronaut.serde.Encoder;
import io.micronaut.serde.ObjectSerializer;
import io.micronaut.serde.Serializer;
Expand Down Expand Up @@ -47,15 +46,9 @@
@Internal
final class CustomizedObjectSerializer<T> implements ObjectSerializer<T> {
private final SerBean<T> serBean;
private final List<SerBean.SerProperty<T, Object>> writeProperties;

CustomizedObjectSerializer(SerBean<T> serBean) {
this(serBean, serBean.writeProperties);
}

public CustomizedObjectSerializer(SerBean<T> serBean, List<SerBean.SerProperty<T, Object>> writeProperties) {
this.serBean = serBean;
this.writeProperties = writeProperties;
}

@Override
Expand Down Expand Up @@ -87,7 +80,7 @@ public void serializeInto(Encoder encoder, EncoderContext context, Argument<? ex
}

private void serializeIntoInternal(Encoder objectEncoder, T objectValue, EncoderContext context) throws IOException {
for (SerBean.SerProperty<T, Object> property : writeProperties) {
for (SerBean.SerProperty<T, Object> property : serBean.writeProperties) {
final Object propertyValue = property.get(objectValue);
final String backRef = property.backRef;
if (backRef != null) {
Expand Down Expand Up @@ -150,7 +143,7 @@ private void serializeIntoInternal(Encoder objectEncoder, T objectValue, Encoder
);
}
try {
if (property.unwrapped) {
if (property.serializableInto) {
if (property.objectSerializer != null) {
property.objectSerializer.serializeInto(objectEncoder, context, property.argument, propertyValue);
} else {
Expand All @@ -170,31 +163,6 @@ private void serializeIntoInternal(Encoder objectEncoder, T objectValue, Encoder
}
}
}
final SerBean.SerProperty<T, Object> anyGetter = serBean.anyGetter;
if (anyGetter != null) {
final Object data = anyGetter.get(objectValue);
if (data instanceof Map<?, ?> map) {
if (CollectionUtils.isNotEmpty(map)) {
for (Object k : map.keySet()) {
final Object v = map.get(k);
objectEncoder.encodeKey(k.toString());
if (v == null) {
objectEncoder.encodeNull();
} else {
Argument<?> valueType = anyGetter.argument.getTypeVariable("V")
.orElse(null);
if (valueType == null || valueType.equalsType(Argument.OBJECT_ARGUMENT)) {
valueType = Argument.of(v.getClass());
}
@SuppressWarnings("unchecked")
Serializer<Object> foundSerializer = (Serializer<Object>) context.findSerializer(valueType);
final Serializer<Object> serializer = foundSerializer.createSpecific(context, valueType);
serializer.serialize(objectEncoder, context, valueType, v);
}
}
}
}
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,31 +18,22 @@
import io.micronaut.context.BeanContext;
import io.micronaut.context.annotation.BootstrapContextCompatible;
import io.micronaut.context.annotation.Primary;
import io.micronaut.core.annotation.AnnotationMetadata;
import io.micronaut.core.annotation.Internal;
import io.micronaut.core.annotation.NonNull;
import io.micronaut.core.annotation.Nullable;
import io.micronaut.core.beans.exceptions.IntrospectionException;
import io.micronaut.core.type.Argument;
import io.micronaut.core.type.GenericPlaceholder;
import io.micronaut.core.util.ArrayUtils;
import io.micronaut.core.util.CollectionUtils;
import io.micronaut.core.util.SupplierUtil;
import io.micronaut.serde.SerdeIntrospections;
import io.micronaut.serde.Serializer;
import io.micronaut.serde.config.SerializationConfiguration;
import io.micronaut.serde.config.annotation.SerdeConfig;
import io.micronaut.serde.exceptions.SerdeException;
import io.micronaut.serde.support.util.BeanDefKey;
import io.micronaut.serde.util.CustomizableSerializer;
import jakarta.inject.Singleton;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;

Expand Down Expand Up @@ -112,55 +103,26 @@ private io.micronaut.serde.ObjectSerializer<Object> createSpecificInternal(Encod
return new RuntimeTypeSerializer(encoderContext, e, type);
}

final AnnotationMetadata annotationMetadata = type.getAnnotationMetadata();
if (serBean.hasJsonValue()) {
return new JsonValueSerializer<>(serBean.jsonValue);
}
if (annotationMetadata.isAnnotationPresent(SerdeConfig.SerIgnored.class) || annotationMetadata.isAnnotationPresent(
SerdeConfig.META_ANNOTATION_PROPERTY_ORDER) || annotationMetadata.isAnnotationPresent(SerdeConfig.SerIncluded.class)) {
final String[] ignored = annotationMetadata.stringValues(SerdeConfig.SerIgnored.class);
final String[] included = annotationMetadata.stringValues(SerdeConfig.SerIncluded.class);
List<String> order = Arrays.asList(annotationMetadata.stringValues(SerdeConfig.META_ANNOTATION_PROPERTY_ORDER));
final boolean hasIgnored = ArrayUtils.isNotEmpty(ignored);
final boolean hasIncluded = ArrayUtils.isNotEmpty(included);
Set<String> ignoreSet = hasIgnored ? CollectionUtils.setOf(ignored) : null;
Set<String> includedSet = hasIncluded ? CollectionUtils.setOf(included) : null;
if (!order.isEmpty() || hasIgnored || hasIncluded) {
return createIgnoringCustomObjectSerializer(serBean, order, hasIgnored, hasIncluded, ignoreSet, includedSet);
}
}
io.micronaut.serde.ObjectSerializer<Object> outer;
io.micronaut.serde.ObjectSerializer<Object> serializer;
if (serBean.simpleBean) {
outer = new SimpleObjectSerializer<>(serBean);
serializer = new SimpleObjectSerializer<>(serBean);
} else if (serBean.jsonValue != null) {
serializer = new JsonValueSerializer<>(serBean.jsonValue);
} else {
outer = new CustomizedObjectSerializer<>(serBean);
serializer = new CustomizedObjectSerializer<>(serBean);
}
if (serBean.subtyped) {
outer = new RuntimeTypeSerializer(encoderContext, outer, type);
serializer = new RuntimeTypeSerializer(encoderContext, serializer, type);
}
if (serBean.wrapperProperty != null) {
outer = new WrappedObjectSerializer<>(outer, serBean.wrapperProperty);
}
return outer;
}

private static CustomizedObjectSerializer<Object> createIgnoringCustomObjectSerializer(SerBean<Object> serBean, List<String> order, boolean hasIgnored, boolean hasIncluded, Set<String> ignoreSet, Set<String> includedSet) {
final List<SerBean.SerProperty<Object, Object>> writeProperties = new ArrayList<>(serBean.writeProperties);
if (!order.isEmpty()) {
writeProperties.sort(Comparator.comparingInt(o -> order.indexOf(o.name)));
}
if (hasIgnored) {
writeProperties.removeIf(p -> ignoreSet.contains(p.name));
}
if (hasIncluded) {
writeProperties.removeIf(p -> !includedSet.contains(p.name));
serializer = new WrappedObjectSerializer<>(serializer, serBean.wrapperProperty);
}
return new CustomizedObjectSerializer<>(serBean, writeProperties);
return serializer;
}

private <T> SerBean<T> getSerializableBean(Argument<T> type, @Nullable String namePrefix, @Nullable String nameSuffix, EncoderContext context) throws SerdeException {
BeanDefKey key = new BeanDefKey(type, namePrefix, nameSuffix);
// Use suppliers to prevent recursive update because the lambda will can call the same method again
// Use suppliers to prevent recursive update because the lambda will call the same method again
Supplier<SerBean<?>> serBeanSupplier = serBeanMap.computeIfAbsent(key, ignore -> SupplierUtil.memoizedNonEmpty(() -> {
try {
return new SerBean<>((Argument<Object>) type, introspections, context, configuration, namePrefix, nameSuffix, beanContext);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import io.micronaut.core.util.ArrayUtils;
import io.micronaut.core.value.OptionalMultiValues;
import io.micronaut.serde.Encoder;
import io.micronaut.serde.ObjectSerializer;
import io.micronaut.serde.Serializer;
import io.micronaut.serde.config.SerializationConfiguration;
import io.micronaut.serde.exceptions.SerdeException;
Expand All @@ -44,50 +45,58 @@ public OptionalMultiValuesSerializer(SerializationConfiguration jacksonConfigura
}

@Override
public Serializer<OptionalMultiValues<V>> createSpecific(EncoderContext context, Argument<? extends OptionalMultiValues<V>> type) throws SerdeException {
public ObjectSerializer<OptionalMultiValues<V>> createSpecific(EncoderContext context, Argument<? extends OptionalMultiValues<V>> type) throws SerdeException {
final Argument[] generics = type.getTypeParameters();
if (ArrayUtils.isEmpty(generics)) {
throw new SerdeException("Cannot serialize raw OptionalMultiValues");
}
final Argument generic = generics[0];
final Argument<Object> generic = generics[0];
final Argument listGeneric = Argument.listOf(generic);
Serializer listSerializer = context.findSerializer(listGeneric).createSpecific(context, listGeneric);
Serializer valueSerializer = context.findSerializer(generic).createSpecific(context, generic);
return new Serializer<OptionalMultiValues<V>>() {
@Override
public void serialize(Encoder encoder, EncoderContext context, Argument<? extends OptionalMultiValues<V>> type, OptionalMultiValues<V> value) throws IOException {
Objects.requireNonNull(value, "Values can't be null");

Encoder objectEncoder = encoder.encodeObject(type);
return new ObjectSerializer<>() {

Serializer<Object> listSerializer = context.findSerializer(listGeneric).createSpecific(context, listGeneric);
Serializer<Object> valueSerializer = context.findSerializer(generic).createSpecific(context, generic);

@Override
public void serializeInto(Encoder encoder, EncoderContext context, Argument<? extends OptionalMultiValues<V>> type, OptionalMultiValues<V> value) throws IOException {
for (CharSequence key : value) {
Optional<? extends List<V>> opt = value.get(key);
if (opt.isPresent()) {
String fieldName = key.toString();
objectEncoder.encodeKey(fieldName);
encoder.encodeKey(fieldName);
List<V> list = opt.get();
if (alwaysSerializeErrorsAsList) {
listSerializer.serialize(
objectEncoder,
context,
listGeneric, list
encoder,
context,
listGeneric, list
);
} else {
if (list.size() == 1) {
valueSerializer.serialize(
objectEncoder,
context,
generic, list.get(0)
encoder,
context,
generic, list.get(0)
);
} else {
listSerializer.serialize(
objectEncoder,
context,
listGeneric, list
encoder,
context,
listGeneric, list
);
}
}
}
}
}

@Override
public void serialize(Encoder encoder, EncoderContext context, Argument<? extends OptionalMultiValues<V>> type, OptionalMultiValues<V> value) throws IOException {
Objects.requireNonNull(value, "Values can't be null");

Encoder objectEncoder = encoder.encodeObject(type);
serializeInto(encoder, context, type, value);
objectEncoder.finishStructure();
}

Expand Down
Loading
Loading