Skip to content

Commit

Permalink
[eclipse-ee4j#616]Suggested solution for recognizing @JsonbProperty a…
Browse files Browse the repository at this point in the history
…nnotation on enum constants

Signed-off-by: Anton Pinsky <anton.pinsky@ionos.com>
  • Loading branch information
api-from-the-ion committed Oct 8, 2023
1 parent 170103e commit 8061172
Show file tree
Hide file tree
Showing 7 changed files with 140 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -503,7 +503,7 @@ private ModelDeserializer<JsonParser> typeDeserializer(Class<?> rawType,
ModelDeserializer<Object> delegate,
Set<JsonParser.Event> events) {
return TypeDeserializers
.getTypeDeserializer(rawType, customization, jsonbContext.getConfigProperties(), delegate, events);
.getTypeDeserializer(rawType, customization, jsonbContext, delegate, events);
}

private Class<?> resolveImplClass(Class<?> rawType, Customization customization) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,51 @@
package org.eclipse.yasson.internal.deserializer.types;

import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;

import org.eclipse.yasson.internal.DeserializationContextImpl;
import org.eclipse.yasson.internal.model.ClassModel;
import org.eclipse.yasson.internal.model.PropertyModel;

/**
* Deserializer of the {@link Enum}.
*/
class EnumDeserializer extends TypeDeserializer {

EnumDeserializer(TypeDeserializerBuilder builder) {
super(builder);
}
private final Map<String, ? extends Enum<?>> nameToConstantMap;

@SuppressWarnings("unchecked")
@Override
Object deserializeStringValue(String value, DeserializationContextImpl context, Type rType) {
return Enum.valueOf((Class<Enum>) rType, value);
}
EnumDeserializer(TypeDeserializerBuilder builder) {
super(builder);

nameToConstantMap = createNameToConstantMap(builder);
}

private static <E extends Enum<E>> Map<String, E> createNameToConstantMap(TypeDeserializerBuilder builder) {
Map<String, E> nameToConstantMap = null;
Class<?> clazz = builder.getClazz();

if (clazz.isEnum()) {
try {
@SuppressWarnings("unchecked")
Class<E> enumClazz = (Class<E>) clazz;
nameToConstantMap = new HashMap<>();
ClassModel classModel = builder.getJsonbContext().getMappingContext().getOrCreateClassModel(clazz);

for (E enumConstant : enumClazz.getEnumConstants()) {
PropertyModel model = classModel.getPropertyModel(enumConstant.name());
nameToConstantMap.put(model.getReadName(), enumConstant);
}
} catch (ClassCastException classCastException) {
throw new IllegalArgumentException("EnumDeserializer can only be used with Enum types");
}
}
return nameToConstantMap;
}

@SuppressWarnings({"unchecked", "rawtypes"})
@Override
Object deserializeStringValue(String value, DeserializationContextImpl context, Type rType) {
return nameToConstantMap == null ? Enum.valueOf((Class<Enum>) rType, value) : nameToConstantMap.get(value);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import java.util.Objects;

import org.eclipse.yasson.internal.JsonbConfigProperties;
import org.eclipse.yasson.internal.JsonbContext;
import org.eclipse.yasson.internal.deserializer.ModelDeserializer;
import org.eclipse.yasson.internal.model.customization.ClassCustomization;
import org.eclipse.yasson.internal.model.customization.Customization;
Expand All @@ -23,16 +24,16 @@ class TypeDeserializerBuilder {

private final Class<?> clazz;
private final Customization customization;
private final JsonbConfigProperties configProperties;
private final JsonbContext jsonbContext;
private final ModelDeserializer<Object> delegate;

TypeDeserializerBuilder(Class<?> clazz,
Customization customization,
JsonbConfigProperties configProperties,
JsonbContext jsonbContext,
ModelDeserializer<Object> delegate) {
this.clazz = Objects.requireNonNull(clazz);
this.customization = customization == null ? ClassCustomization.empty() : customization;
this.configProperties = configProperties;
this.jsonbContext = jsonbContext;
this.delegate = Objects.requireNonNull(delegate);
}

Expand All @@ -41,7 +42,11 @@ public Class<?> getClazz() {
}

public JsonbConfigProperties getConfigProperties() {
return configProperties;
return jsonbContext.getConfigProperties();
}

public JsonbContext getJsonbContext() {
return jsonbContext;
}

public ModelDeserializer<Object> getDelegate() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
import jakarta.json.bind.JsonbException;
import jakarta.json.stream.JsonParser;

import org.eclipse.yasson.internal.JsonbConfigProperties;
import org.eclipse.yasson.internal.JsonbContext;
import org.eclipse.yasson.internal.deserializer.JustReturn;
import org.eclipse.yasson.internal.deserializer.ModelDeserializer;
import org.eclipse.yasson.internal.deserializer.NullCheckDeserializer;
Expand Down Expand Up @@ -135,22 +135,22 @@ private TypeDeserializers() {
*
* @param clazz type to create deserializer for
* @param customization type customization
* @param properties config properties
* @param jsonbContext config properties
* @param delegate delegate to be called by the created deserializer
* @param events expected parser events at the beginning when deserializing the type
* @return type deserializer
*/
public static ModelDeserializer<JsonParser> getTypeDeserializer(Class<?> clazz,
Customization customization,
JsonbConfigProperties properties,
JsonbContext jsonbContext,
ModelDeserializer<Object> delegate,
Set<JsonParser.Event> events) {
JsonParser.Event[] eventArray = events.toArray(new JsonParser.Event[0]);
if (OPTIONAL_TYPES.containsKey(clazz)) {
Class<?> optionalType = OPTIONAL_TYPES.get(clazz);
TypeDeserializerBuilder builder = new TypeDeserializerBuilder(optionalType,
customization,
properties,
jsonbContext,
JustReturn.instance());
ValueExtractor valueExtractor = new ValueExtractor(DESERIALIZERS.get(optionalType).apply(builder));
PositionChecker positionChecker = new PositionChecker(valueExtractor, clazz, eventArray);
Expand All @@ -165,7 +165,7 @@ public static ModelDeserializer<JsonParser> getTypeDeserializer(Class<?> clazz,
}
}

TypeDeserializerBuilder builder = new TypeDeserializerBuilder(clazz, customization, properties, delegate);
TypeDeserializerBuilder builder = new TypeDeserializerBuilder(clazz, customization, jsonbContext, delegate);
if (DESERIALIZERS.containsKey(clazz)) {
ValueExtractor valueExtractor = new ValueExtractor(DESERIALIZERS.get(clazz).apply(builder));
return new NullCheckDeserializer(new PositionChecker(valueExtractor, clazz, eventArray), delegate);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,26 +12,56 @@

package org.eclipse.yasson.internal.serializer.types;

import jakarta.json.stream.JsonGenerator;
import java.util.EnumMap;

import org.eclipse.yasson.internal.SerializationContextImpl;
import org.eclipse.yasson.internal.model.ClassModel;
import org.eclipse.yasson.internal.model.PropertyModel;

import jakarta.json.stream.JsonGenerator;

/**
* Serializer of the {@link Enum} types.
*/
class EnumSerializer extends TypeSerializer<Enum<?>> {

EnumSerializer(TypeSerializerBuilder serializerBuilder) {
super(serializerBuilder);
}
private final EnumMap<? extends Enum<?>, String> constantToNameMap;

EnumSerializer(TypeSerializerBuilder serializerBuilder) {
super(serializerBuilder);

constantToNameMap = createConstantToNameMap(serializerBuilder);
}

private static <E extends Enum<E>> EnumMap<E, String> createConstantToNameMap(TypeSerializerBuilder serializerBuilder) {
EnumMap<E, String> constantToNameMap = null;
Class<?> clazz = serializerBuilder.getClazz();

if (clazz.isEnum()) {
try{
@SuppressWarnings("unchecked")
Class<E> enumClazz = (Class<E>) clazz;
constantToNameMap = new EnumMap<>(enumClazz);
ClassModel classModel = serializerBuilder.getJsonbContext().getMappingContext().getOrCreateClassModel(clazz);

for (E enumConstant : enumClazz.getEnumConstants()) {
PropertyModel model = classModel.getPropertyModel(enumConstant.name());
constantToNameMap.put(enumConstant, model.getWriteName());
}
} catch (ClassCastException classCastException) {
throw new IllegalArgumentException("EnumSerializer can only be used with Enum types");
}
}
return constantToNameMap;
}

@Override
void serializeValue(Enum<?> value, JsonGenerator generator, SerializationContextImpl context) {
generator.write(value.name());
}
@Override
void serializeValue(Enum<?> value, JsonGenerator generator, SerializationContextImpl context) {
generator.write(constantToNameMap == null ? value.name() : constantToNameMap.get(value));
}

@Override
void serializeKey(Enum<?> key, JsonGenerator generator, SerializationContextImpl context) {
generator.writeKey(key.name());
}
@Override
void serializeKey(Enum<?> key, JsonGenerator generator, SerializationContextImpl context) {
generator.writeKey(constantToNameMap == null ? key.name() : constantToNameMap.get(key));
}
}
23 changes: 23 additions & 0 deletions src/test/java/org/eclipse/yasson/serializers/SerializersTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
import org.eclipse.yasson.serializers.model.Author;
import org.eclipse.yasson.serializers.model.Box;
import org.eclipse.yasson.serializers.model.BoxWithAnnotations;
import org.eclipse.yasson.serializers.model.Cars;
import org.eclipse.yasson.serializers.model.Colors;
import org.eclipse.yasson.serializers.model.Containee;
import org.eclipse.yasson.serializers.model.Container;
Expand Down Expand Up @@ -824,4 +825,26 @@ void testCustomSerializersAndDeserializersInEnum(){
assertEquals(expected, jsonb.fromJson(expectedJson, Colors.class));
}

@Test
void testJsonbPropertyInEnum(){
Jsonb jsonb = JsonbBuilder.create();

Cars expected = Cars.FORD;

String expectedJson = jsonb.toJson(expected);

assertEquals(expected, jsonb.fromJson(expectedJson, Cars.class));
}

@Test
void testNoJsonbPropertyInEnum(){
Jsonb jsonb = JsonbBuilder.create();

Cars expected = Cars.FIAT;

String expectedJson = jsonb.toJson(expected);

assertEquals(expected, jsonb.fromJson(expectedJson, Cars.class));
}

}
22 changes: 22 additions & 0 deletions src/test/java/org/eclipse/yasson/serializers/model/Cars.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Copyright (c) 2023 Oracle 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.serializers.model;

import jakarta.json.bind.annotation.JsonbProperty;

public enum Cars {
@JsonbProperty("Ford")
FORD,
FIAT
}

0 comments on commit 8061172

Please sign in to comment.