Include Jackson JSON serializers for BigDecimals and BigIntegers as well as generating typeless Collection JSON.

Resolves gh-67.
This commit is contained in:
John Blum
2020-05-08 01:44:39 -07:00
parent 33458ab4be
commit a855787b66
3 changed files with 273 additions and 0 deletions

View File

@@ -0,0 +1,58 @@
/*
* Copyright 2020 the original author or authors.
*
* 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
*
* https://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.springframework.geode.jackson.databind.serializer;
import java.io.IOException;
import java.math.BigDecimal;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
import com.fasterxml.jackson.databind.ser.std.NumberSerializers;
/**
* The {@link BigDecimalSerializer} class is a {@link NumberSerializers.Base} serializer
* for serializing {@link BigDecimal} values.
*
* @author John Blum
* @see java.math.BigDecimal
* @see com.fasterxml.jackson.databind.ser.std.NumberSerializers
* @since 1.3.0
*/
@SuppressWarnings("unused")
public class BigDecimalSerializer extends NumberSerializers.Base<BigDecimal> {
public static final BigDecimalSerializer INSTANCE = new BigDecimalSerializer();
public BigDecimalSerializer() {
super(BigDecimal.class, JsonParser.NumberType.BIG_DECIMAL, "bigdecimal");
}
@Override
public void serialize(BigDecimal value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider)
throws IOException {
jsonGenerator.writeNumber(value);
}
@Override
public void serializeWithType(BigDecimal value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider,
TypeSerializer typeSerializer) throws IOException {
serialize(value, jsonGenerator, serializerProvider);
}
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright 2020 the original author or authors.
*
* 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
*
* https://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.springframework.geode.jackson.databind.serializer;
import java.io.IOException;
import java.math.BigInteger;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
import com.fasterxml.jackson.databind.ser.std.NumberSerializers;
/**
* The {@link BigIntegerSerializer} class is a {@link NumberSerializers.Base} serializer
* for serializing {@link BigInteger} values.
*
* @author John Blum
* @see java.math.BigInteger
* @see com.fasterxml.jackson.databind.ser.std.NumberSerializers
* @since 1.3.0
*/
@SuppressWarnings("unused")
public class BigIntegerSerializer extends NumberSerializers.Base<BigInteger> {
public static final BigIntegerSerializer INSTANCE = new BigIntegerSerializer();
public BigIntegerSerializer() {
super(BigInteger.class, JsonParser.NumberType.BIG_INTEGER, "biginteger");
}
@Override
public void serialize(BigInteger value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider)
throws IOException {
jsonGenerator.writeNumber(value);
}
@Override
public void serializeWithType(BigInteger value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider,
TypeSerializer typeSerializer) throws IOException {
serialize(value, jsonGenerator, serializerProvider);
}
}

View File

@@ -0,0 +1,157 @@
/*
* Copyright 2020 the original author or authors.
*
* 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
*
* https://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.springframework.geode.jackson.databind.serializer;
import java.io.IOException;
import java.util.Collection;
import java.util.Objects;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
import com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap;
import com.fasterxml.jackson.databind.ser.std.AsArraySerializerBase;
import com.fasterxml.jackson.databind.ser.std.CollectionSerializer;
import org.springframework.data.gemfire.util.CollectionUtils;
/**
* The {@link TypelessCollectionSerializer} class is a custom, typeless {@link CollectionSerializer} implementation.
*
* This {@link AsArraySerializerBase} implementation is a lot like {@link CollectionSerializer}, however it excludes
* unnecessary type metadata in the context of GemFire/Geode.
*
* @author John Blum
* @see java.util.Collection
* @see com.fasterxml.jackson.core.JsonGenerator
* @see com.fasterxml.jackson.databind.JsonSerializer
* @see com.fasterxml.jackson.databind.ObjectMapper
* @see com.fasterxml.jackson.databind.SerializerProvider
* @see com.fasterxml.jackson.databind.ser.std.AsArraySerializerBase
* @since 1.3.0
*/
@SuppressWarnings("unused")
public class TypelessCollectionSerializer extends AsArraySerializerBase<Collection<?>> {
protected static final boolean DEFAULT_UNWRAP_SINGLE = false;
protected static final boolean DEFAULT_STATIC_TYPING = false;
public TypelessCollectionSerializer(ObjectMapper mapper) {
super(Collection.class, mapper.getTypeFactory().constructType(Object.class), DEFAULT_STATIC_TYPING,
null, null);
}
public TypelessCollectionSerializer(TypelessCollectionSerializer serializer, BeanProperty property,
TypeSerializer typeSerializer, JsonSerializer<?> elementSerializer) {
super(serializer, property, typeSerializer, elementSerializer, DEFAULT_UNWRAP_SINGLE);
}
@Override
public void serializeWithType(Collection<?> value, JsonGenerator jsonGenerator,
SerializerProvider serializerProvider, TypeSerializer typeSerializer) throws IOException {
serialize(value, jsonGenerator, serializerProvider);
}
@Override
public boolean hasSingleElement(Collection<?> value) {
return value != null && value.size() == 1;
}
@Override
protected void serializeContents(Collection<?> value, JsonGenerator jsonGenerator,
SerializerProvider serializerProvider) throws IOException {
jsonGenerator.setCurrentValue(value);
PropertySerializerMap serializers = this._dynamicSerializers;
TypeSerializer typeSerializer = this._valueTypeSerializer;
int index = -1;
try {
for (Object element : CollectionUtils.nullSafeCollection(value)) {
index++;
if (Objects.isNull(element)) {
serializerProvider.defaultSerializeNull(jsonGenerator);
}
else {
Class<?> elementType = element.getClass();
JsonSerializer<Object> serializer = resolveSerializer(serializerProvider, elementType);
if (typeSerializer != null) {
serializer.serializeWithType(element, jsonGenerator, serializerProvider, typeSerializer);
}
else {
serializer.serialize(element, jsonGenerator, serializerProvider);
}
}
}
}
catch(Exception cause) {
wrapAndThrow(serializerProvider, cause, value, index);
}
}
private JavaType constructSpecializedType(SerializerProvider serializerProvider, JavaType baseType, Class<?> subclass) {
return serializerProvider.constructSpecializedType(baseType, subclass);
}
private JsonSerializer<Object> resolveSerializer(SerializerProvider serializerProvider, Class<?> type)
throws JsonMappingException {
JsonSerializer<Object> resolvedSerializer = this._elementSerializer;
if (Objects.isNull(resolvedSerializer)) {
PropertySerializerMap dynamicSerializers = this._dynamicSerializers;
resolvedSerializer = dynamicSerializers.serializerFor(type);
if (Objects.isNull(resolvedSerializer)) {
resolvedSerializer = Objects.nonNull(this._elementType) && this._elementType.hasGenericTypes()
? this._findAndAddDynamic(dynamicSerializers,
constructSpecializedType(serializerProvider, this._elementType, type), serializerProvider)
: this._findAndAddDynamic(dynamicSerializers, type, serializerProvider);
}
}
return resolvedSerializer;
}
@Override
public TypelessCollectionSerializer withResolved(BeanProperty property, TypeSerializer typeSerializer,
JsonSerializer<?> elementSerializer, Boolean unwrapSingle) {
return new TypelessCollectionSerializer(this, property, typeSerializer, elementSerializer);
}
@Override
public TypelessCollectionSerializer _withValueTypeSerializer(TypeSerializer typeSerializer) {
return new TypelessCollectionSerializer(this, this._property, typeSerializer, this._elementSerializer);
}
}