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

nl.vpro.jackson2.Jackson2Mapper Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2012 All rights reserved
 * VPRO The Netherlands
 */
package nl.vpro.jackson2;

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.event.Level;

import java.io.Serial;
import java.util.function.Consumer;
import java.util.function.Predicate;


import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.json.JsonReadFeature;
import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.introspect.AnnotationIntrospectorPair;
import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
import com.fasterxml.jackson.databind.ser.PropertyFilter;
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.module.jakarta.xmlbind.JakartaXmlBindAnnotationIntrospector;
import com.google.common.annotations.Beta;

import nl.vpro.logging.Slf4jHelper;

/**
 * TODO: Many static public members that are not unmodifiable (e.g. {@link #INSTANCE}).
 * 

* Please use the static getters (like {@link #getInstance()}, so we could change that. * * @author Rico * @author Michiel Meeuwissen */ @Slf4j public class Jackson2Mapper extends ObjectMapper { @Serial private static final long serialVersionUID = 8353430660109292010L; private static boolean loggedAboutAvro = false; private static boolean loggedAboutFallback = false; private static final SimpleFilterProvider FILTER_PROVIDER = new SimpleFilterProvider(); @Deprecated public static final Jackson2Mapper INSTANCE = getInstance(); @Deprecated public static final Jackson2Mapper LENIENT = getLenientInstance(); @Deprecated public static final Jackson2Mapper STRICT = getStrictInstance(); @Deprecated public static final Jackson2Mapper PRETTY_STRICT = getPrettyStrictInstance(); @Deprecated public static final Jackson2Mapper PRETTY = getPrettyInstance(); @Deprecated public static final Jackson2Mapper PUBLISHER = getPublisherInstance(); @Deprecated public static final Jackson2Mapper PRETTY_PUBLISHER = getPublisherInstance(); @Deprecated public static final Jackson2Mapper BACKWARDS_PUBLISHER = getBackwardsPublisherInstance(); private static final ThreadLocal THREAD_LOCAL = ThreadLocal.withInitial(Jackson2Mapper::getInstance); public static Jackson2Mapper getInstance() { Jackson2Mapper mapper = new Jackson2Mapper("instance"); mapper.setConfig(mapper.getSerializationConfig().withView(Views.Forward.class)); mapper.setConfig(mapper.getDeserializationConfig().withView(Views.Forward.class)); return mapper; } public static Jackson2Mapper getLenientInstance() { Jackson2Mapper lenient = new Jackson2Mapper("lenient"); lenient.enable(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL); lenient.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); lenient.setConfig(lenient.getSerializationConfig().withView(Views.Forward.class)); lenient.setConfig(lenient.getDeserializationConfig().withView(Views.Forward.class)); return lenient; } public static Jackson2Mapper getPrettyInstance() { Jackson2Mapper pretty = new Jackson2Mapper("pretty"); pretty.enable(SerializationFeature.INDENT_OUTPUT); return pretty; } public static Jackson2Mapper getPrettyStrictInstance() { Jackson2Mapper pretty_and_strict = new Jackson2Mapper("pretty_strict"); pretty_and_strict.enable(SerializationFeature.INDENT_OUTPUT); pretty_and_strict.setConfig(pretty_and_strict.getSerializationConfig().withView(Views.Forward.class)); pretty_and_strict.setConfig(pretty_and_strict.getDeserializationConfig().withView(Views.Forward.class)); pretty_and_strict.enable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); // This gives quite a lot of troubles. Though I'd like it to be set, especially because PRETTY is used in tests. return pretty_and_strict; } public static Jackson2Mapper getStrictInstance() { Jackson2Mapper strict = new Jackson2Mapper("strict"); strict.enable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); strict.setConfig(strict.getSerializationConfig().withView(Views.Forward.class)); strict.setConfig(strict.getDeserializationConfig().withView(Views.Forward.class)); return strict; } public static Jackson2Mapper getPublisherInstance() { Jackson2Mapper publisher = new Jackson2Mapper("publisher"); publisher.setConfig(publisher.getSerializationConfig().withView(Views.ForwardPublisher.class)); publisher.setConfig(publisher.getDeserializationConfig().withView(Views.Forward.class)); return publisher; } public static Jackson2Mapper getPrettyPublisherInstance() { Jackson2Mapper prettyPublisher = new Jackson2Mapper("pretty_publisher"); prettyPublisher.setConfig(prettyPublisher.getSerializationConfig().withView(Views.ForwardPublisher.class)); prettyPublisher.setConfig(prettyPublisher.getDeserializationConfig().withView(Views.Forward.class)); prettyPublisher.enable(SerializationFeature.INDENT_OUTPUT); return prettyPublisher; } public static Jackson2Mapper getBackwardsPublisherInstance() { Jackson2Mapper backwardsPublisher = new Jackson2Mapper("backwards_publisher"); backwardsPublisher.setConfig(backwardsPublisher.getSerializationConfig().withView(Views.Publisher.class)); backwardsPublisher.setConfig(backwardsPublisher.getDeserializationConfig().withView(Views.Normal.class)); backwardsPublisher.enable(SerializationFeature.INDENT_OUTPUT); return backwardsPublisher; } @Beta public static Jackson2Mapper getModelInstance() { Jackson2Mapper model = new Jackson2Mapper("model"); model.setConfig(model.getSerializationConfig().withView(Views.Model.class)); return model; } @Beta public static Jackson2Mapper getModelAndNormalInstance() { Jackson2Mapper modalAndNormal = new Jackson2Mapper("model_and_normal"); modalAndNormal.setConfig(modalAndNormal.getSerializationConfig().withView(Views.ModelAndNormal.class)); return modalAndNormal; } public static Jackson2Mapper getThreadLocal() { return THREAD_LOCAL.get(); } public static void setThreadLocal(Jackson2Mapper set) { if (set == null) { THREAD_LOCAL.remove(); } else { THREAD_LOCAL.set(set); } } @SneakyThrows({JsonProcessingException.class}) public static T lenientTreeToValue(JsonNode jsonNode, Class clazz) { return getLenientInstance().treeToValue(jsonNode, clazz); } private final String toString; private Jackson2Mapper(String toString, Predicate predicate) { configureMapper(this, predicate); this.toString = toString; } private Jackson2Mapper(String toString) { configureMapper(this); this.toString = toString; } @SafeVarargs public static Jackson2Mapper create(String toString, Predicate module, Consumer... consumer) { Jackson2Mapper result = new Jackson2Mapper(toString, module); for (Consumer c : consumer){ c.accept(result); } return result; } public static void configureMapper(ObjectMapper mapper) { configureMapper(mapper, m -> true); } public static void configureMapper(ObjectMapper mapper, Predicate filter) { mapper.setFilterProvider(FILTER_PROVIDER); AnnotationIntrospector introspector = new AnnotationIntrospectorPair( new JacksonAnnotationIntrospector(), new JakartaXmlBindAnnotationIntrospector(mapper.getTypeFactory()) ); mapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY); mapper.setAnnotationIntrospector(introspector); mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); // This seems a good idea when reading from couchdb or so, but when reading user supplied forms, it is confusing not getting errors. mapper.enable(JsonParser.Feature.ALLOW_SINGLE_QUOTES); mapper.enable(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES); mapper.enable(MapperFeature.USE_WRAPPER_NAME_AS_PROPERTY_NAME); try { // this should nbe needed, but if I don't do this, resteasy still doesn't allow comments mapper.enable(JsonParser.Feature.ALLOW_COMMENTS); mapper.setConfig(mapper.getDeserializationConfig().with(JsonReadFeature.ALLOW_LEADING_ZEROS_FOR_NUMBERS)); mapper.setConfig(mapper.getDeserializationConfig().with(JsonReadFeature.ALLOW_JAVA_COMMENTS)); } catch (NoClassDefFoundError noClassDefFoundError) { Slf4jHelper.log(log, loggedAboutFallback ? Level.DEBUG : Level.WARN, noClassDefFoundError.getMessage() + " temporary falling back. Please upgrade jackson"); loggedAboutFallback = true; mapper.enable(JsonParser.Feature.ALLOW_COMMENTS); //noinspection deprecation mapper.enable(JsonParser.Feature.ALLOW_NUMERIC_LEADING_ZEROS); } register(mapper, filter, new JavaTimeModule()); register(mapper, filter, new DateModule()); // For example normal support for Optional. Jdk8Module jdk8Module = new Jdk8Module(); // jdk8Module.configureAbsentsAsNulls(true); This I think it covered by com.fasterxml.jackson.annotation.JsonInclude.Include.NON_ABSENT register(mapper, filter, jdk8Module); mapper.setConfig(mapper.getSerializationConfig().withView(Views.Normal.class)); mapper.setConfig(mapper.getDeserializationConfig().withView(Views.Normal.class)); //SimpleModule module = new SimpleModule(); //module.setDeserializerModifier(new AfterUnmarshalModifier()); //mapper.registerModule(module); try { Class avro = Class.forName("nl.vpro.jackson2.SerializeAvroModule"); register(mapper, filter, (com.fasterxml.jackson.databind.Module) avro.newInstance()); } catch (ClassNotFoundException ncdfe) { if (! loggedAboutAvro) { log.debug("SerializeAvroModule could not be registered because: " + ncdfe.getClass().getName() + " " + ncdfe.getMessage()); } loggedAboutAvro = true; } catch (IllegalAccessException | InstantiationException e) { log.error(e.getMessage(), e); loggedAboutAvro = true; } try { Class guava = Class.forName("nl.vpro.jackson2.GuavaRangeModule"); register(mapper, filter, (com.fasterxml.jackson.databind.Module) guava.newInstance()); } catch (ClassNotFoundException ncdfe) { log.debug(ncdfe.getMessage()); } catch (IllegalAccessException | InstantiationException e) { log.error(e.getMessage(), e); } } public static void addFilter(String key, PropertyFilter filter) { FILTER_PROVIDER.addFilter(key, filter); log.info("Installed filter {} -> {}", key, filter); } private static void register(ObjectMapper mapper, Predicate predicate, Module module) { if (predicate.test(module)) { mapper.registerModule(module); } } @Override public String toString() { return Jackson2Mapper.class.getSimpleName() + " (" + toString + ")"; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy