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

com.zengtengpeng.codec.MyJsonJacksonCodec Maven / Gradle / Ivy

The newest version!
/**
 * Copyright (c) 2013-2021 Nikita Koksharov
 *
 * 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 com.zengtengpeng.codec;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import com.fasterxml.jackson.core.JsonGenerator.Feature;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.ObjectMapper.DefaultTypeResolverBuilder;
import com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping;
import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.ByteBufInputStream;
import io.netty.buffer.ByteBufOutputStream;
import org.redisson.client.codec.BaseCodec;
import org.redisson.client.handler.State;
import org.redisson.client.protocol.Decoder;
import org.redisson.client.protocol.Encoder;
import org.redisson.codec.CborJacksonCodec;
import org.redisson.codec.MsgPackJacksonCodec;

import javax.xml.datatype.XMLGregorianCalendar;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;

/**
 * Json codec based on Jackson implementation.
 * https://github.com/FasterXML/jackson
 * 

* Fully thread-safe. * * @see CborJacksonCodec * @see MsgPackJacksonCodec * * @author Nikita Koksharov * */ public class MyJsonJacksonCodec extends BaseCodec { public static final MyJsonJacksonCodec INSTANCE = new MyJsonJacksonCodec(); @JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="@id") @JsonAutoDetect(fieldVisibility = Visibility.ANY, getterVisibility = Visibility.PUBLIC_ONLY, setterVisibility = Visibility.NONE, isGetterVisibility = Visibility.NONE) public static class ThrowableMixIn { } protected final ObjectMapper mapObjectMapper; private final Encoder encoder = new Encoder() { @Override public ByteBuf encode(Object in) throws IOException { ByteBuf out = ByteBufAllocator.DEFAULT.buffer(); try { ByteBufOutputStream os = new ByteBufOutputStream(out); mapObjectMapper.writeValue((OutputStream) os, in); return os.buffer(); } catch (IOException e) { out.release(); throw e; } catch (Exception e) { out.release(); throw new IOException(e); } } }; private final Decoder decoder = new Decoder() { @Override public Object decode(ByteBuf buf, State state) throws IOException { return mapObjectMapper.readValue((InputStream) new ByteBufInputStream(buf), Object.class); } }; public MyJsonJacksonCodec() { this(new ObjectMapper()); } public MyJsonJacksonCodec(ClassLoader classLoader) { this(createObjectMapper(classLoader, new ObjectMapper())); } public MyJsonJacksonCodec(ClassLoader classLoader, MyJsonJacksonCodec codec) { this(createObjectMapper(classLoader, codec.mapObjectMapper.copy())); } private static boolean warmedup = false; private void warmup() { if (getValueEncoder() == null || getValueDecoder() == null || warmedup) { return; } warmedup = true; try { ByteBuf d = getValueEncoder().encode("testValue"); getValueDecoder().decode(d, null); d.release(); } catch (IOException e) { e.printStackTrace(); } } protected static ObjectMapper createObjectMapper(ClassLoader classLoader, ObjectMapper om) { TypeFactory tf = TypeFactory.defaultInstance().withClassLoader(classLoader); om.setTypeFactory(tf); return om; } public MyJsonJacksonCodec(ObjectMapper mapObjectMapper) { this.mapObjectMapper = mapObjectMapper.copy(); init(this.mapObjectMapper); initTypeInclusion(this.mapObjectMapper); warmup(); } protected void initTypeInclusion(ObjectMapper mapObjectMapper) { TypeResolverBuilder mapTyper = new DefaultTypeResolverBuilder(DefaultTyping.NON_FINAL) { public boolean useForType(JavaType t) { switch (_appliesFor) { case NON_CONCRETE_AND_ARRAYS: while (t.isArrayType()) { t = t.getContentType(); } // fall through case OBJECT_AND_NON_CONCRETE: return (t.getRawClass() == Object.class) || !t.isConcrete(); case NON_FINAL: while (t.isArrayType()) { t = t.getContentType(); } // to fix problem with wrong long to int conversion if (t.getRawClass() == Long.class) { return true; } if (t.getRawClass() == XMLGregorianCalendar.class) { return false; } return !t.isFinal(); // includes Object.class default: // case JAVA_LANG_OBJECT: return t.getRawClass() == Object.class; } } }; mapTyper.init(JsonTypeInfo.Id.CLASS, null); mapTyper.inclusion(JsonTypeInfo.As.PROPERTY); mapObjectMapper.setDefaultTyping(mapTyper); } protected void init(ObjectMapper objectMapper) { objectMapper.setSerializationInclusion(Include.NON_NULL); objectMapper.setVisibility(objectMapper.getSerializationConfig() .getDefaultVisibilityChecker() .withFieldVisibility(Visibility.ANY) .withGetterVisibility(Visibility.NONE) .withSetterVisibility(Visibility.NONE) .withCreatorVisibility(Visibility.NONE)); objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); objectMapper.enable(Feature.WRITE_BIGDECIMAL_AS_PLAIN); objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); objectMapper.enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY); objectMapper.addMixIn(Throwable.class, ThrowableMixIn.class); JavaTimeModule javaTimeModule = new JavaTimeModule(); javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern("HH:mm:ss"))); javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern("HH:mm:ss"))); objectMapper.registerModule(javaTimeModule); } @Override public Decoder getValueDecoder() { return decoder; } @Override public Encoder getValueEncoder() { return encoder; } @Override public ClassLoader getClassLoader() { if (mapObjectMapper.getTypeFactory().getClassLoader() != null) { return mapObjectMapper.getTypeFactory().getClassLoader(); } return super.getClassLoader(); } public ObjectMapper getObjectMapper() { return mapObjectMapper; } }