/* * Copyright 2015-2016 the original author or authors. * * Licensed 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.springframework.data.rest.webmvc.json; import java.io.IOException; import java.util.ArrayList; import java.util.List; import org.springframework.data.rest.webmvc.json.JsonSchema.EnumProperty; import org.springframework.data.rest.webmvc.json.JsonSchema.JsonSchemaProperty; import org.springframework.data.util.TypeInformation; import org.springframework.util.Assert; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.BeanProperty; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.deser.ContextualDeserializer; import com.fasterxml.jackson.databind.deser.std.StdDeserializer; import com.fasterxml.jackson.databind.module.SimpleDeserializers; import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.module.SimpleSerializers; import com.fasterxml.jackson.databind.ser.std.StdSerializer; /** * Custom Spring Data REST Jackson serializers. * * @author Oliver Gierke * @since 2.4 * @soundtrack Wallis Bird - I Could Be Your Man (Yeah! Wallis Bird Live 2007-2014) */ public class JacksonSerializers extends SimpleModule { private static final long serialVersionUID = 4396776390917947147L; /** * Creates a new {@link JacksonSerializers} with the given {@link EnumTranslator}. * * @param translator must not be {@literal null}. */ public JacksonSerializers(EnumTranslator translator) { Assert.notNull(translator, "EnumTranslator must not be null!"); SimpleSerializers serializers = new SimpleSerializers(); serializers.addSerializer(Enum.class, new EnumTranslatingSerializer(translator)); setSerializers(serializers); SimpleDeserializers deserializers = new SimpleDeserializers(); deserializers.addDeserializer(Enum.class, new EnumTranslatingDeserializer(translator)); setDeserializers(deserializers); } /** * An enum serializer to translate raw enum values into values resolved through a resource bundle. * * @author Oliver Gierke */ @SuppressWarnings("rawtypes") public static class EnumTranslatingSerializer extends StdSerializer<Enum> implements JsonSchemaPropertyCustomizer { private static final long serialVersionUID = -6706924011396258646L; private final EnumTranslator translator; /** * Creates a new {@link EnumTranslatingSerializer} using the given {@link EnumTranslator}. * * @param translator must not be {@literal null}. */ public EnumTranslatingSerializer(EnumTranslator translator) { super(Enum.class); Assert.notNull(translator, "EnumTranslator must not be null!"); this.translator = translator; } /* * (non-Javadoc) * @see com.fasterxml.jackson.databind.ser.std.StdSerializer#serialize(java.lang.Object, com.fasterxml.jackson.core.JsonGenerator, com.fasterxml.jackson.databind.SerializerProvider) */ @Override public void serialize(Enum value, JsonGenerator gen, SerializerProvider provider) throws IOException { gen.writeString(translator.asText(value)); } /* * (non-Javadoc) * @see org.springframework.data.rest.webmvc.json.JsonSchemaPropertyCustomizer#customize(org.springframework.data.rest.webmvc.json.JsonSchema.JsonSchemaProperty, org.springframework.data.util.TypeInformation) */ @Override public JsonSchemaProperty customize(JsonSchemaProperty property, TypeInformation<?> type) { List<String> values = new ArrayList<String>(); for (Object value : type.getType().getEnumConstants()) { values.add(translator.asText((Enum<?>) value)); } return ((EnumProperty) property).withValues(values); } } /** * Enum deserializer that uses a resource bundle to resolve enum values. * * @author Oliver Gierke */ @SuppressWarnings("rawtypes") public static class EnumTranslatingDeserializer extends StdDeserializer<Enum> implements ContextualDeserializer { private static final long serialVersionUID = 5305284644923180079L; private final EnumTranslator translator; private final BeanProperty property; /** * Creates a new {@link EnumTranslatingDeserializer} using the given {@link EnumTranslator}. * * @param translator must not be {@literal null}. */ public EnumTranslatingDeserializer(EnumTranslator translator) { this(translator, null); } /** * Creates a new {@link EnumTranslatingDeserializer} using the given {@link EnumTranslator} and {@link BeanProperty} * . * * @param translator must not be {@literal null}. * @param property can be {@literal null}. */ public EnumTranslatingDeserializer(EnumTranslator translator, BeanProperty property) { super(Enum.class); Assert.notNull(translator, "EnumTranslator must not be null!"); this.translator = translator; this.property = property; } /* * (non-Javadoc) * @see com.fasterxml.jackson.databind.deser.ContextualDeserializer#createContextual(com.fasterxml.jackson.databind.DeserializationContext, com.fasterxml.jackson.databind.BeanProperty) */ @Override public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException { return new EnumTranslatingDeserializer(translator, property); } /* * (non-Javadoc) * @see com.fasterxml.jackson.databind.JsonDeserializer#deserialize(com.fasterxml.jackson.core.JsonParser, com.fasterxml.jackson.databind.DeserializationContext) */ @Override @SuppressWarnings("unchecked") public Enum deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException { if (property == null) { throw new IllegalStateException("Can only translate enum with property information!"); } return translator.fromText((Class<? extends Enum<?>>) getActualType(property.getType()).getRawClass(), p.getText()); } /** * Returns the value types for containers or the original type otherwise. * * @param type must not be {@literal null}. * @return */ private static JavaType getActualType(JavaType type) { return type.isContainerType() ? type.getContentType() : type; } } }