能否使用Jackson基于Java类生成类型树而非值树?
解决方案
Jackson可以实现该需求,这里提供两种可行方案:
方案1:直接基于类的类型元数据构建类型树(推荐)
你不需要实例化类对象,直接通过Jackson提供的Bean内省能力读取类的字段类型信息,按需生成对应类型的JsonNode即可,完全规避null值的影响:
package org.example.jackson.typetree; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition; import com.fasterxml.jackson.databind.node.ObjectNode; import org.junit.jupiter.api.Test; import java.util.List; public class TypeTreeTest { static class SomeClass{ public Integer integer; public String string; } @Test void buildTypeTree() { ObjectMapper objectMapper = new ObjectMapper(); // 内省目标类的元信息 JavaType javaType = objectMapper.getTypeFactory().constructType(SomeClass.class); List<BeanPropertyDefinition> properties = objectMapper.getSerializationConfig() .introspect(javaType) .findProperties(); // 手动构建类型对应的JsonNode树 ObjectNode rootNode = objectMapper.createObjectNode(); for (BeanPropertyDefinition prop : properties) { Class<?> rawType = prop.getRawPrimaryType(); if (rawType == Integer.class || rawType == int.class) { rootNode.put(prop.getName(), 0); // 生成IntNode } else if (rawType == String.class) { rootNode.put(prop.getName(), ""); // 生成TextNode } // 其他类型可按需扩展 } // 验证节点类型 rootNode.fields().forEachRemaining(child -> { if(child.getValue().isIntegralNumber() || child.getValue().isTextual()){ System.out.println("Nice!"); }else if(child.getValue().isNull()){ System.out.println("Booooh...!"); } }); } }
该方案完全基于类型元数据生成节点,不受字段实际值影响,也不需要反射实例化类,性能和稳定性更高。
方案2:自定义Null值序列化器
如果你一定要走valueToTree的序列化流程,可以为不同Java类型注册对应的Null序列化器,在字段值为null时输出对应类型的默认值:
package org.example.jackson.typetree; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.module.SimpleModule; import org.junit.jupiter.api.Test; import java.io.IOException; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; public class CustomNullSerializeTest { static class SomeClass{ public Integer integer; public String string; } @Test void extractTypeTree() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { ObjectMapper objectMapper = new ObjectMapper(); SimpleModule module = new SimpleModule(); // 为Integer类型注册null序列化器,输出0 module.addSerializer(Integer.class, new JsonSerializer<Integer>() { @Override public void serialize(Integer value, JsonGenerator gen, SerializerProvider serializers) throws IOException { gen.writeNumber(value == null ? 0 : value); } }); // 为String类型注册null序列化器,输出空字符串 module.addSerializer(String.class, new JsonSerializer<String>() { @Override public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException { gen.writeString(value == null ? "" : value); } }); objectMapper.registerModule(module); final Constructor<?> constructor = SomeClass.class.getDeclaredConstructor(); final Object o = constructor.newInstance((Object[]) null); final var jsonNode = objectMapper.valueToTree(o); final var fields = jsonNode.fields(); while(fields.hasNext()){ final var child = fields.next(); if(child.getValue().isIntegralNumber() || child.getValue().isTextual()){ System.out.println("Nice!"); }else if(child.getValue().isNull()){ System.out.println("Booooh...!"); } } } }
运行上述两种方案的代码,都会输出两次Nice!,符合预期。
内容的提问来源于stack exchange,提问作者Byebye
相关产品推荐
相关产品推荐

