我找到了一个适用于 immuatble (lombok @Value) 模型的解决方案:
Object
使用不写 Option.None的所有使用 mixIn 的过滤器(请参阅下面的“解决方案”)
- 当缺少相应的 json 条目时,我现有的
ObjectMapper
(带有JavaslangModule
)已经将 None 设置为 Option 字段
编码
import static org.assertj.core.api.Assertions.assertThat;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonFilter;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.PropertyWriter;
import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import javaslang.control.Option;
import javaslang.jackson.datatype.JavaslangModule;
import lombok.AllArgsConstructor;
import lombok.Value;
import org.junit.Test;
import java.io.IOException;
import java.lang.reflect.Field;
public class JsonModelAndSerialization {
// Write to Json
// =============
private static ObjectMapper objectMapper = new ObjectMapper()
.registerModule(new Jdk8Module())
.registerModule(new JavaslangModule())
// not required but provide forward compatibility on new field
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
static String write(Object data) throws JsonProcessingException {
SimpleBeanPropertyFilter filter = new NoneOptionPropertyFilter();
objectMapper.addMixIn(Object.class, NoneOptionFilter.class);
final SimpleFilterProvider filters = new SimpleFilterProvider().setDefaultFilter(filter);
ObjectWriter writer = objectMapper.writer(filters);
return writer.writeValueAsString(data);
}
// Filter classes
// ==============
@JsonFilter("Filter None")
private static class NoneOptionFilter {}
private static class NoneOptionPropertyFilter extends SimpleBeanPropertyFilter {
@Override
public void serializeAsField(
Object pojo, JsonGenerator jgen,
SerializerProvider provider, PropertyWriter writer) throws Exception{
Field field = pojo.getClass().getDeclaredField(writer.getName());
if(field.getType().equals(Option.class)){
field.setAccessible(true);
Option<?> value = (Option<?>) field.get(pojo);
if(value.isEmpty()) return;
}
super.serializeAsField(pojo, jgen, provider, writer);
}
}
// Usage example
// =============
// **important note**
// For @Value deserialization, a lombok config file should be added
// in the source folder of the model class definition
// with content:
// lombok.anyConstructor.addConstructorProperties = true
@Value
@AllArgsConstructor(onConstructor_={@JsonCreator})
public static class StringInt {
private int intValue;
private Option<String> stringValue;
}
@Value
@AllArgsConstructor(onConstructor_={@JsonCreator})
public static class StringIntPair {
private StringInt item1;
private StringInt item2;
}
@Test
public void readWriteMyClass() throws IOException {
StringIntPair myClass = new StringIntPair(
new StringInt(6 * 9, Option.some("foo")),
new StringInt( 42, Option.none()));
String json = write(myClass);
// {"item1":{"intValue":54,"stringValue":"foo"},"item2":{"intValue":42}}
StringIntPair myClass2 = objectMapper.readValue(json, StringIntPair.class);
assertThat(myClass2).isEqualTo(myClass);
}
}
优点:
- 有时减小 json 的大小
Option.None
(因此在模型中添加选项字段在不使用时不会花费大小)
- 稍后在模型中添加具有 Option 类型的字段时,它提供向后读取兼容性(默认为
None
)
缺点:
- 无法区分具有 None 字段值的正确数据和错误忘记字段的不正确数据。我认为这是完全可以接受的。