MinifyingHttpMessageConverter.java

/*
 * Copyright 2021 Global Crop Diversity Trust
 *
 * 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.gringlobal.spring;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.introspect.ClassIntrospector;
import com.fasterxml.jackson.databind.module.SimpleModule;
import org.gringlobal.custom.json.serializer.SortJsonSerializer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;

import java.io.IOException;

/**
 * Custom HttpMessageConverter produces a JSON that doesn't contain properties with a NULL values.
 * Also, it excludes properties: "ownedBy", "ownedDate", "createdBy", "createdDate", "modifiedBy", "modifiedDate".
 */
public class MinifyingHttpMessageConverter extends MappingJackson2HttpMessageConverter {

	public static final String APPLICATION_MINIFIED_JSON_VALUE = "application/minified-json";
	public static final MediaType APPLICATION_MINIFIED_JSON = MediaType.valueOf(APPLICATION_MINIFIED_JSON_VALUE);

	public MinifyingHttpMessageConverter(ObjectMapper objectMapper) {
		final SimpleModule customModule = new SimpleModule().addSerializer(new SortJsonSerializer());
		final ObjectMapper customObjectMapper = objectMapper.registerModule(customModule).copy();
		customObjectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);

		@JsonIgnoreProperties({ "ownedBy", "ownedDate", "createdBy", "createdDate", "modifiedBy", "modifiedDate" })
		abstract class MixIn {
		}

		customObjectMapper.setMixInResolver(new ClassIntrospector.MixInResolver() {
			@Override
			public Class<?> findMixInClassFor(Class<?> cls) {
				return MixIn.class;
			}

			@Override
			public ClassIntrospector.MixInResolver copy() {
				return this;
			}
		});

		super.setObjectMapper(customObjectMapper);
	}

	@Override
	protected boolean canRead(MediaType mediaType) {
		// Never use this converter to read JSON input
		return false;
	}

	@Override
	protected boolean canWrite(MediaType mediaType) {
		return mediaType != null && (mediaType.isCompatibleWith(APPLICATION_MINIFIED_JSON));
	}

	@Override
	protected void addDefaultHeaders(HttpHeaders headers, Object o, MediaType contentType) throws IOException {
		// override produced content type to `application/json`
		super.addDefaultHeaders(headers, o, MediaType.APPLICATION_JSON);
	}
}