All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.teamapps.dto.ObjectSerializer Maven / Gradle / Ivy

There is a newer version: 0.9.194
Show newest version
/*-
 * ========================LICENSE_START=================================
 * TeamApps
 * ---
 * Copyright (C) 2014 - 2023 TeamApps.org
 * ---
 * 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.
 * =========================LICENSE_END==================================
 */
package org.teamapps.dto;

import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
import com.fasterxml.jackson.databind.jsontype.impl.StdTypeResolverBuilder;
import com.fasterxml.jackson.databind.ser.std.EnumSerializer;

import java.io.IOException;
import java.util.List;
import java.util.Map;

public class ObjectSerializer extends JsonSerializer {

	private final StdTypeResolverBuilder typeResolverBuilder;
	private final TeamAppsJacksonTypeIdResolver typeIdResolver;
	private final CopyOnWriteLeakyCache, TypeSerializer> typeSerializersCache = new CopyOnWriteLeakyCache<>();
	private final CopyOnWriteLeakyCache, BeanDescription> beanDescriptionsCache = new CopyOnWriteLeakyCache<>();

	public ObjectSerializer() {
		typeIdResolver = new TeamAppsJacksonTypeIdResolver();
		typeResolverBuilder = new ObjectMapper.DefaultTypeResolverBuilder(ObjectMapper.DefaultTyping.NON_FINAL)
				.inclusion(JsonTypeInfo.As.PROPERTY)
				.init(JsonTypeInfo.Id.CLASS, typeIdResolver)
				.typeProperty("_type");
	}

	public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
		if (value == null) {
			gen.writeNull();
		} else if (value instanceof List) {
			gen.writeStartArray();
			for (Object entry : (List) value) {
				serialize(entry, gen, serializers);
			}
			gen.writeEndArray();
		} else if (value instanceof Map) {
			gen.writeStartObject();
			for (Map.Entry entry : ((Map) value).entrySet()) {
				gen.writeFieldName(entry.getKey());
				serialize(entry.getValue(), gen, serializers);
			}
			gen.writeEndObject();
		} else if (value instanceof Enum) {
			JsonSerializer valueSerializer = serializers.findValueSerializer(value.getClass());
			if (!(valueSerializer instanceof EnumSerializer)) {
				// this is a custom serializer. Prefer custom serializers!
				valueSerializer.serialize(value, gen, serializers);
			} else {
				serializeEnum((Enum) value, gen, serializers);
			}
		} else {
			TypeSerializer typeSerializer = typeSerializersCache.computeIfAbsent(value.getClass(), clazz -> typeResolverBuilder.buildTypeSerializer(serializers.getConfig(), serializers
					.getTypeFactory().constructType(clazz), null));
			if (typeSerializer != null) {
				serializeWithType(value, gen, serializers, typeSerializer);
			} else {
				gen.writeObject(value);
			}
		}
	}

	public void serializeWithType(Object value, JsonGenerator gen, SerializerProvider serializers, TypeSerializer typeSer) throws IOException {
		if (value instanceof List || value instanceof Map) {
			serialize(value, gen, serializers);
		} else {
			serializers.findValueSerializer(value.getClass()).serializeWithType(value, gen, serializers, typeSer);
		}
	}

	public void serializeEnum(Enum value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
		gen.writeStartObject();
		gen.writeFieldName("_type");
		gen.writeString(typeIdResolver.idFromClass(value.getDeclaringClass()));
		gen.writeFieldName("_name");
		gen.writeString(value.name());

		BeanDescription beanDescription = beanDescriptionsCache.computeIfAbsent(value.getClass(), aClass -> {
			JavaType javaType = serializers.getTypeFactory().constructType(aClass);
			return serializers.getConfig().introspect(javaType);
		});

		for (BeanPropertyDefinition property : beanDescription.findProperties()) {
			if (property.getName().equals("declaringClass")) {
				continue;
			}
			Object propertyValue = property.getAccessor().getValue(value);
			if (propertyValue == null) {
				continue; // skip null values
			}
			JsonSerializer serializer = serializers.findValueSerializer(propertyValue.getClass());
			if (serializer.isEmpty(serializers, propertyValue)) {
				continue;
			}

			gen.writeFieldName(property.getName());
			serializer.serialize(propertyValue, gen, serializers);
		}

		gen.writeEndObject();
	}
}