Firestore嵌套泛型类出现No setter/field警告及存储异常求助
问题分析:MyOptional类序列化/反序列化警告排查
问题现象
日志中持续出现以下警告:
No setter/field for present found on class MyOptional (fields/setters are case sensitive!) No setter/field for empty found on class MyOptional (fields/setters are case sensitive!) No setter/field for f1 found on class MyOptional No setter/field for f2 found on class MyOptional ...以及其他字段的同类警告
其中前两条警告关联MyOptional的静态方法,后两条关联Foo类的字段。同时数据库中实际存储的是Foo对象而非MyOptional,还意外包含了isEmpty()和isPresent()方法的结果。
代码结构
MyOptional类
@Data @NoArgsConstructor @AllArgsConstructor @JsonIgnoreProperties(ignoreUnknown = true) public class MyOptional<T> { private static final FSOptional<?> EMPTY = new FSOptional<>(null); private T value; public static <T> FSOptional<T> empty() { return (FSOptional<T>) EMPTY; } public static <T> FSOptional<T> of(T t) { return new FSOptional<>(t); } public boolean isPresent() { return value != null; } public boolean isEmpty() { return value == null; } }
Foo类
@Data @Builder @NoArgsConstructor @AllArgsConstructor @JsonIgnoreProperties(ignoreUnknown = true) public class Foo { private String f1; private String f2; ... }
原因分析
- 静态方法与布尔方法的序列化混淆:Jackson(假设使用的是Jackson进行序列化/反序列化)会把
isPresent()、isEmpty()这类布尔型getter方法序列化为present、empty字段,但MyOptional类中并没有对应的成员变量或setter,反序列化时找不到匹配项就会抛出警告。 - 泛型对象的字段穿透问题:数据库中本应存储MyOptional内部的Foo对象,但序列化/反序列化过程中错误地把Foo的字段(f1、f2等)当成MyOptional的字段处理,说明你在持久化或数据转换时,直接传入了MyOptional对象而非其内部的
value属性。 - 注解配置的局限性:
@JsonIgnoreProperties(ignoreUnknown = true)仅在反序列化时忽略未知字段,但序列化时Jackson仍会根据getter生成present、empty字段写入数据库,反过来反序列化时又因为MyOptional没有这些字段触发警告。
解决办法
- 自定义Jackson序列化/反序列化规则:为MyOptional类添加专用的序列化器和反序列化器,只处理内部的
value字段:@JsonSerialize(using = MyOptionalSerializer.class) @JsonDeserialize(using = MyOptionalDeserializer.class) public class MyOptional<T> { // 原有代码不变 } // 序列化器:只序列化value属性 class MyOptionalSerializer extends JsonSerializer<MyOptional<?>> { @Override public void serialize(MyOptional<?> value, JsonGenerator gen, SerializerProvider serializers) throws IOException { gen.writeObject(value.getValue()); } } // 反序列化器:将JSON转换为MyOptional对象 class MyOptionalDeserializer extends JsonDeserializer<MyOptional<?>> { @Override public MyOptional<?> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { Object value = p.readValueAs(Object.class); return MyOptional.of(value); } } - 忽略布尔方法的序列化:用
@JsonIgnore标记isPresent()和isEmpty()方法,阻止Jackson将它们序列化为字段:@JsonIgnore public boolean isPresent() { return value != null; } @JsonIgnore public boolean isEmpty() { return value == null; } - 修正持久化逻辑:确保持久化时存储的是MyOptional的
value属性(即Foo对象),而非MyOptional本身。比如在DAO层或数据转换环节,调用myOptional.getValue()获取实际对象后再执行持久化操作。 - 替换为原生Optional(可选):如果业务场景允许,直接使用Java原生的
java.util.Optional,Jackson对其有成熟的序列化/反序列化支持,无需自定义逻辑。
内容的提问来源于stack exchange,提问作者mr nooby noob
相关产品推荐
相关产品推荐

