Jackson2Utils.java

package top.infra.jackson2;

import static java.util.stream.Collectors.toList;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;

import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.BeanUtils;
import org.springframework.core.OrderComparator;

import java.io.IOException;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;

import top.infra.common.ClassUtils;

/**
 * Created by zhanghaolun on 16/6/9.
 */
@Slf4j
public abstract class Jackson2Utils {

    private static Boolean JACKSON2_PRESENT;

    static {
        JACKSON2_PRESENT = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper")
            && ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator");
    }

    private Jackson2Utils() {
    }

    /**
     * Setup a Mapper.
     *
     * @param properties   jackson2Properties
     * @param objectMapper ObjectMapper or XmlMapper to customize
     * @return objectMapper
     */
    public static ObjectMapper customize(final Jackson2Properties properties, final ObjectMapper objectMapper) {
        for (final Jackson2MapperCustomizer customizer : jackson2MapperCustomizers()) {
            log.info("customize objectMapper: '{}' using: '{}'.", objectMapper, customizer);
            customizer.customize(properties, objectMapper);
        }
        return objectMapper;
    }

    public static <T> Function<String, T> fromJson( //
        final ObjectMapper objectMapper, //
        final TypeReference<T> typeReference //
    ) {
        return string -> {
            try {
                return objectMapper.readValue(string, typeReference);
            } catch (final IOException wrapped) {
                throw new RuntimeJsonProcessingException("error read from JSON.", wrapped);
            }
        };
    }

    /**
     * parse JSON.
     *
     * @param objectMapper objectMapper
     * @param type         type
     * @param <T>          type
     * @return object
     */
    public static <T> Function<String, T> fromJson( //
        final ObjectMapper objectMapper, //
        final Class<T> type //
    ) {
        return string -> {
            try {
                return objectMapper.readValue(string, type);
            } catch (final IOException wrapped) {
                throw new RuntimeJsonProcessingException("error read from JSON.", wrapped);
            }
        };
    }

    public static Boolean isJackson2Present() {
        return Jackson2Utils.JACKSON2_PRESENT;
    }

    static List<Jackson2MapperCustomizer> jackson2MapperCustomizers() {
        final List<Jackson2MapperCustomizerFactory> factories = jackson2MapperCustomizerFactories();

        return factories.stream()
            .map(factory -> factory.getObject().orElse(null))
            .filter(Objects::nonNull)
            .sorted(OrderComparator.INSTANCE)
            .collect(toList());
    }

    static List<Jackson2MapperCustomizerFactory> jackson2MapperCustomizerFactories() {
        final String basePackage = Jackson2Utils.class.getName().split("\\.")[0];

        final Set<Class<Jackson2MapperCustomizerFactory>> classes = ClassUtils.FileAndClasspathUtils.scan(basePackage,
            new ClassUtils.FileAndClasspathUtils.AssignableFilter(Jackson2MapperCustomizerFactory.class, false, false));

        log.info("found {} jackson2MapperCustomizerFactories.", classes.size());

        return classes.stream().map(BeanUtils::instantiate).collect(toList());
    }

    /**
     * to JSON.
     *
     * @param objectMapper objectMapper
     * @param <T>          type
     * @return JSON
     */
    public static <T> Function<T, String> toJson(final ObjectMapper objectMapper) {
        return object -> {
            try {
                if (object != null) {
                    return objectMapper.writeValueAsString(object);
                } else {
                    return "";
                }
            } catch (final JsonProcessingException wrapped) {
                throw new RuntimeJsonProcessingException("error serialize to JSON.", wrapped);
            }
        };
    }

    public static <T> String toJson(final ObjectMapper objectMapper, final T item) {
        return toJson(objectMapper).apply(item);
    }

    public static class RuntimeJsonProcessingException extends RuntimeException {

        private static final long serialVersionUID = 1L;

        public RuntimeJsonProcessingException(final String message, final Throwable cause) {
            super(message, cause);
        }
    }
}