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

software.xdev.mockserver.serialization.ObjectMapperFactory Maven / Gradle / Ivy

There is a newer version: 1.0.8
Show newest version
/*
 * Copyright © 2024 XDEV Software (https://xdev.software)
 *
 * 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 software.xdev.mockserver.serialization;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.json.JsonReadFeature;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;

import software.xdev.mockserver.serialization.deserializers.body.BodyDTODeserializer;
import software.xdev.mockserver.serialization.deserializers.body.BodyWithContentTypeDTODeserializer;
import software.xdev.mockserver.serialization.deserializers.collections.CookiesDeserializer;
import software.xdev.mockserver.serialization.deserializers.collections.HeadersDeserializer;
import software.xdev.mockserver.serialization.deserializers.collections.ParametersDeserializer;
import software.xdev.mockserver.serialization.deserializers.condition.TimeToLiveDTODeserializer;
import software.xdev.mockserver.serialization.deserializers.condition.VerificationTimesDTODeserializer;
import software.xdev.mockserver.serialization.deserializers.request.RequestDefinitionDTODeserializer;
import software.xdev.mockserver.serialization.deserializers.string.NottableStringDeserializer;
import software.xdev.mockserver.serialization.serializers.body.BinaryBodyDTOSerializer;
import software.xdev.mockserver.serialization.serializers.body.BinaryBodySerializer;
import software.xdev.mockserver.serialization.serializers.body.ParameterBodyDTOSerializer;
import software.xdev.mockserver.serialization.serializers.body.ParameterBodySerializer;
import software.xdev.mockserver.serialization.serializers.body.RegexBodyDTOSerializer;
import software.xdev.mockserver.serialization.serializers.body.RegexBodySerializer;
import software.xdev.mockserver.serialization.serializers.body.StringBodyDTOSerializer;
import software.xdev.mockserver.serialization.serializers.body.StringBodySerializer;
import software.xdev.mockserver.serialization.serializers.collections.CookiesSerializer;
import software.xdev.mockserver.serialization.serializers.collections.HeadersSerializer;
import software.xdev.mockserver.serialization.serializers.collections.ParametersSerializer;
import software.xdev.mockserver.serialization.serializers.condition.VerificationTimesDTOSerializer;
import software.xdev.mockserver.serialization.serializers.condition.VerificationTimesSerializer;
import software.xdev.mockserver.serialization.serializers.request.HttpRequestDTOSerializer;
import software.xdev.mockserver.serialization.serializers.response.HttpResponseDTOSerializer;
import software.xdev.mockserver.serialization.serializers.response.HttpResponseSerializer;
import software.xdev.mockserver.serialization.serializers.response.TimeToLiveDTOSerializer;
import software.xdev.mockserver.serialization.serializers.response.TimeToLiveSerializer;
import software.xdev.mockserver.serialization.serializers.response.TimesDTOSerializer;
import software.xdev.mockserver.serialization.serializers.response.TimesSerializer;
import software.xdev.mockserver.serialization.serializers.string.NottableStringSerializer;


@SuppressWarnings({"unchecked", "rawtypes"})
public final class ObjectMapperFactory
{
	private static ObjectMapper objectMapper =
		buildObjectMapperWithDeserializerAndSerializers(Collections.emptyList(), Collections.emptyList(), false);
	private static final ObjectWriter PRETTY_PRINT_WRITER =
		buildObjectMapperWithDeserializerAndSerializers(
			Collections.emptyList(),
			Collections.emptyList(),
			false).writerWithDefaultPrettyPrinter();
	private static final ObjectWriter PRETTY_PRINT_WRITER_THAT_SERIALISES_DEFAULT_FIELDS =
		buildObjectMapperWithDeserializerAndSerializers(
			Collections.emptyList(),
			Collections.emptyList(),
			true).writerWithDefaultPrettyPrinter();
	private static final ObjectWriter WRITER = buildObjectMapperWithDeserializerAndSerializers(
		Collections.emptyList(),
		Collections.emptyList(),
		false).writer();
	
	public static ObjectMapper createObjectMapper()
	{
		if(objectMapper == null)
		{
			objectMapper = buildObjectMapperWithDeserializerAndSerializers(
				Collections.emptyList(),
				Collections.emptyList(),
				false);
		}
		return objectMapper;
	}
	
	public static ObjectMapper createObjectMapper(final JsonDeserializer... replacementJsonDeserializers)
	{
		if(replacementJsonDeserializers == null || replacementJsonDeserializers.length == 0)
		{
			if(objectMapper == null)
			{
				objectMapper = buildObjectMapperWithDeserializerAndSerializers(
					Collections.emptyList(),
					Collections.emptyList(),
					false);
			}
			return objectMapper;
		}
		else
		{
			return buildObjectMapperWithDeserializerAndSerializers(
				Arrays.asList(replacementJsonDeserializers),
				Collections.emptyList(),
				false);
		}
	}
	
	public static ObjectWriter createObjectMapper(
		final boolean pretty,
		final boolean serialiseDefaultValues,
		final JsonSerializer... additionJsonSerializers)
	{
		if(additionJsonSerializers == null || additionJsonSerializers.length == 0)
		{
			if(pretty && serialiseDefaultValues)
			{
				return PRETTY_PRINT_WRITER_THAT_SERIALISES_DEFAULT_FIELDS;
			}
			else if(pretty)
			{
				return PRETTY_PRINT_WRITER;
			}
			else
			{
				return WRITER;
			}
		}
		else
		{
			if(pretty)
			{
				return buildObjectMapperWithDeserializerAndSerializers(
					Collections.emptyList(),
					Arrays.asList(additionJsonSerializers),
					serialiseDefaultValues).writerWithDefaultPrettyPrinter();
			}
			else
			{
				return buildObjectMapperWithDeserializerAndSerializers(
					Collections.emptyList(),
					Arrays.asList(additionJsonSerializers),
					serialiseDefaultValues).writer();
			}
		}
	}
	
	public static ObjectMapper buildObjectMapperWithoutRemovingEmptyValues()
	{
		return new ObjectMapper(JsonFactory.builder()
			// relax parsing
			.configure(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS, true)
			.configure(JsonReadFeature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER, true)
			.configure(JsonReadFeature.ALLOW_LEADING_ZEROS_FOR_NUMBERS, true)
			.configure(JsonReadFeature.ALLOW_NON_NUMERIC_NUMBERS, true)
			.configure(JsonReadFeature.ALLOW_MISSING_VALUES, true)
			.configure(JsonReadFeature.ALLOW_TRAILING_COMMA, true)
			.build())
			// ignore failures
			.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
			.configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, false)
			.configure(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS, false)
			.configure(DeserializationFeature.FAIL_ON_TRAILING_TOKENS, false)
			.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false)
			// relax parsing
			.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true)
			.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true)
			.configure(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL, true)
			.configure(JsonParser.Feature.ALLOW_COMMENTS, true)
			.configure(JsonParser.Feature.ALLOW_YAML_COMMENTS, true)
			.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true)
			.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true)
			.configure(JsonParser.Feature.IGNORE_UNDEFINED, true)
			// use arrays
			.configure(DeserializationFeature.USE_JAVA_ARRAY_FOR_JSON_ARRAY, true)
			// consistent json output
			.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true);
	}
	
	public static ObjectMapper buildObjectMapperWithOnlyConfigurationDefaults()
	{
		return buildObjectMapperWithoutRemovingEmptyValues()
			// remove empty values from JSON
			.setSerializationInclusion(JsonInclude.Include.NON_DEFAULT)
			.setSerializationInclusion(JsonInclude.Include.NON_NULL)
			.setSerializationInclusion(JsonInclude.Include.NON_EMPTY)
			// add support for java date time serialisation and de-serialisation
			.registerModule(new JavaTimeModule());
	}
	
	private static ObjectMapper buildObjectMapperWithDeserializerAndSerializers(
		final List replacementJsonDeserializers,
		final List replacementJsonSerializers,
		final boolean serialiseDefaultValues)
	{
		final ObjectMapper objectMapper = buildObjectMapperWithOnlyConfigurationDefaults();
		
		// register our own module with our serializers and deserializers
		final SimpleModule module = new SimpleModule();
		addDeserializers(module, replacementJsonDeserializers.toArray(new JsonDeserializer[0]));
		addSerializers(module, replacementJsonSerializers.toArray(new JsonSerializer[0]), serialiseDefaultValues);
		objectMapper.registerModule(module);
		return objectMapper;
	}
	
	private static void addDeserializers(
		final SimpleModule module,
		final JsonDeserializer[] replacementJsonDeserializers)
	{
		final List jsonDeserializers = Arrays.asList(
			// request
			new RequestDefinitionDTODeserializer(),
			// times
			new TimeToLiveDTODeserializer(),
			// request body
			new BodyDTODeserializer(),
			new BodyWithContentTypeDTODeserializer(),
			// condition
			new VerificationTimesDTODeserializer(),
			// nottable string
			new NottableStringDeserializer(),
			// key and multivalue
			new HeadersDeserializer(),
			new ParametersDeserializer(),
			new CookiesDeserializer()
		);
		final Map jsonDeserializersByType = new HashMap<>();
		for(final JsonDeserializer jsonDeserializer : jsonDeserializers)
		{
			jsonDeserializersByType.put(jsonDeserializer.handledType(), jsonDeserializer);
		}
		// override any existing deserializers
		for(final JsonDeserializer additionJsonDeserializer : replacementJsonDeserializers)
		{
			jsonDeserializersByType.put(additionJsonDeserializer.handledType(), additionJsonDeserializer);
		}
		for(final Map.Entry additionJsonDeserializer : jsonDeserializersByType.entrySet())
		{
			module.addDeserializer(additionJsonDeserializer.getKey(), additionJsonDeserializer.getValue());
		}
	}
	
	private static void addSerializers(
		final SimpleModule module,
		final JsonSerializer[] replacementJsonSerializers,
		final boolean serialiseDefaultValues)
	{
		final List jsonSerializers = new ArrayList<>(List.of(
			// times
			new TimesSerializer(),
			new TimesDTOSerializer(),
			new TimeToLiveSerializer(),
			new TimeToLiveDTOSerializer(),
			// request
			new software.xdev.mockserver.serialization.serializers.request.HttpRequestSerializer(),
			new HttpRequestDTOSerializer(),
			// request body
			new BinaryBodySerializer(),
			new BinaryBodyDTOSerializer(),
			new ParameterBodySerializer(),
			new ParameterBodyDTOSerializer(),
			new RegexBodySerializer(),
			new RegexBodyDTOSerializer(),
			new StringBodySerializer(serialiseDefaultValues),
			new StringBodyDTOSerializer(serialiseDefaultValues),
			// condition
			new VerificationTimesDTOSerializer(),
			new VerificationTimesSerializer(),
			// nottable string
			new NottableStringSerializer(),
			// response
			new HttpResponseSerializer(),
			new HttpResponseDTOSerializer(),
			// key and multivalue
			new HeadersSerializer(),
			new ParametersSerializer(),
			new CookiesSerializer()
		));
		customizers().stream()
			.flatMap(c -> c.additionalSerializers().stream())
			.forEach(jsonSerializers::add);
		final Map jsonSerializersByType = new HashMap<>();
		for(final JsonSerializer jsonSerializer : jsonSerializers)
		{
			jsonSerializersByType.put(jsonSerializer.handledType(), jsonSerializer);
		}
		// override any existing serializers
		for(final JsonSerializer additionJsonSerializer : replacementJsonSerializers)
		{
			jsonSerializersByType.put(additionJsonSerializer.handledType(), additionJsonSerializer);
		}
		for(final Map.Entry additionJsonSerializer : jsonSerializersByType.entrySet())
		{
			module.addSerializer(additionJsonSerializer.getKey(), additionJsonSerializer.getValue());
		}
	}
	
	private static List customizers;
	
	private static List customizers()
	{
		if(customizers == null)
		{
			customizers = ServiceLoader.load(ObjectMapperFactoryCustomizer.class).stream()
				.map(ServiceLoader.Provider::get)
				.toList();
		}
		return customizers;
	}
	
	private ObjectMapperFactory()
	{
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy