/**
* Copyright 2014 Nikita Koksharov, Nickolay Borbit
*
* 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.redisson.codec;
import java.nio.ByteBuffer;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectMapper.DefaultTypeResolverBuilder;
import com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder;
/**
*
* @author Nikita Koksharov
*
*/
public class JsonJacksonCodec implements RedissonCodec {
private final ObjectMapper objectMapper = new ObjectMapper();
private ObjectMapper mapObjectMapper = new ObjectMapper();
public JsonJacksonCodec() {
createObjectMapper(objectMapper);
TypeResolverBuilder<?> typer = new DefaultTypeResolverBuilder(DefaultTyping.NON_FINAL);
typer.init(JsonTypeInfo.Id.CLASS, null);
typer.inclusion(JsonTypeInfo.As.PROPERTY);
objectMapper.setDefaultTyping(typer);
createObjectMapper(mapObjectMapper);
// type info inclusion
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;
}
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);
}
private void createObjectMapper(ObjectMapper objectMapper) {
objectMapper.setSerializationInclusion(Include.NON_NULL);
objectMapper.setVisibilityChecker(objectMapper.getSerializationConfig().getDefaultVisibilityChecker()
.withFieldVisibility(JsonAutoDetect.Visibility.ANY)
.withGetterVisibility(JsonAutoDetect.Visibility.NONE)
.withSetterVisibility(JsonAutoDetect.Visibility.NONE)
.withCreatorVisibility(JsonAutoDetect.Visibility.NONE));
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.configure(SerializationFeature.WRITE_BIGDECIMAL_AS_PLAIN, true);
objectMapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true);
}
@Override
public Object decodeKey(ByteBuffer bytes) {
return decode(bytes);
}
@Override
public Object decodeValue(ByteBuffer bytes) {
return decode(bytes);
}
private Object decode(ByteBuffer bytes) {
try {
return objectMapper.readValue(bytes.array(), bytes.arrayOffset() + bytes.position(), bytes.limit(), Object.class);
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
@Override
public byte[] encodeKey(Object key) {
return encodeValue(key);
}
@Override
public byte[] encodeValue(Object value) {
try {
return objectMapper.writeValueAsBytes(value);
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
@Override
public byte[] encodeMapValue(Object value) {
try {
return mapObjectMapper.writeValueAsBytes(value);
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
@Override
public byte[] encodeMapKey(Object key) {
return encodeMapValue(key);
}
@Override
public Object decodeMapValue(ByteBuffer bytes) {
try {
return mapObjectMapper.readValue(bytes.array(), bytes.arrayOffset() + bytes.position(), bytes.limit(), Object.class);
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
@Override
public Object decodeMapKey(ByteBuffer bytes) {
return decodeMapValue(bytes);
}
}