需求背景
最近遇到一个需求,有个数值类型的字段,非必填,默认为空,数据库表针对该字段设计的是一个int类型, 由于dba推荐规范,默认该值是not null。这个时候,问题就来了,数据库默认存的是0,前端展示时,又不能显示这个0(需要的是null)
解决方案
针对此类处理,通常的方案有以下2种:
前端做处理,统一对0和null做处理,0即是null,null即是0
后端做处理,针对要处理的字段,在序列化之前或者之后做处理,或者采取硬编码的方式,针对要处理的字段,写if else
方案分析
针对第一种,这里面有个比较尴尬的地方,前端所接收的字段中,有些0是有意义的,譬如是否有效,0可能代表有效,如果统一做了处理,那这个改为null了,那就出问题了。假如不统一处理,则需要区别对待,由于对前端不熟,不知道是否有类似注解或者带标志的全局方法来处理类似问题,听前端说处理比较麻烦,so,只能后端来处理了。
针对第二种,后端处理的方式更灵活一些,想要简单可拓展,使用@JasonSerilize和@JsonDeserialize注解,写自定义序列化和反序列化类。想要快速完成,走硬编码。起初,因为对jackson的序列化反序列化机制不太了解,写的2个serializer和deserializer发布后也问题不断,所以为了保证项目的进展,采取了比较恶心的硬编码的方式,写了很多if else来做判断
测试序列化
maven依赖:jackson版本2.9.7
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>${jackson.version}</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>${jackson.version}</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>${jackson.version}</version> </dependency>
序列化类:
@JacksonStdImpl public class ZeroToNullSerializer extends JsonSerializer implements ContextualSerializer { private Class<?> type; public ZeroToNullSerializer() { } public ZeroToNullSerializer(final JavaType type) { this.type = type == null ? Object.class : type.getRawClass(); } @Override public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { if (o instanceof Short) { if (((Short) o).compareTo((short)0) == 0) { jsonGenerator.writeNull(); } else { jsonGenerator.writeNumber(((Short) o).shortValue()); } } if (o instanceof Integer) { if (((Integer) o).intValue() == 0) { jsonGenerator.writeNull(); } else { jsonGenerator.writeNumber(((Integer) o).intValue()); } } if (o instanceof Float) { if (((Float) o).compareTo(0f) == 0) { jsonGenerator.writeNull(); } else { jsonGenerator.writeNumber(((Float) o).floatValue()); } } if (o instanceof Double) { if (((Double) o).compareTo(0D) == 0) { jsonGenerator.writeNull(); } else { jsonGenerator.writeNumber(((Double) o).doubleValue()); } } if (o instanceof Long) { if (((Long) o).compareTo(0L) == 0) { jsonGenerator.writeNull(); } else { jsonGenerator.writeNumber(((Long) o).longValue()); } } if (o instanceof BigDecimal) { if (((BigDecimal) o).compareTo(BigDecimal.ZERO) == 0) { jsonGenerator.writeNull(); }else { jsonGenerator.writeNumber((BigDecimal) o); } } } @Override public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException { return new ZeroToNullSerializer(property.getType()); } }
测试序列化的bean:
@Data public class TestSerializerBean { @JsonSerialize(using =ZeroToNullSerializer.class) private Integer number; private Integer age; private BigDecimal money; }
序列化Test:
@Test public void testSerializer() throws JsonProcessingException { TestSerializerBean serializerBean = new TestSerializerBean(); serializerBean.setNumber(0); serializerBean.setAge(0); serializerBean.setMoney(new BigDecimal(20)); String string = mapper.writeValueAsString(serializerBean); System.out.println(string); }
测试结果:
1、待测字段带0:
2、待测字段不带0
@Test public void testSerializer() throws JsonProcessingException { TestSerializerBean serializerBean = new TestSerializerBean(); serializerBean.setNumber(10); serializerBean.setAge(0); serializerBean.setMoney(new BigDecimal(20)); String string = mapper.writeValueAsString(serializerBean); System.out.println(string); }
测试反序列化
反序列化类(仅贴出核心代码, 完整代码我会上传至github, 地址见后文链接):
@Override public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException { if (this.type == Integer.class) { return handleInteger(p, ctxt); } if (this.type == Long.class) { return handleLong(p, ctxt); } if (this.type == BigDecimal.class) { return handleBigDecimal(p, ctxt); } if (this.type == Double.class) { return handleDouble(p, ctxt); } if (this.type == Float.class) { return handleFloat(p, ctxt); } if (this.type == Short.class) { return handleShort(p, ctxt); } throw new RuntimeException("反序列化错误,类型" + type.toString() + "+不支持数值类型的反序列化"); } private Object handleBigDecimal(JsonParser p, DeserializationContext ctxt) throws IOException { switch (p.getCurrentTokenId()) { case JsonTokenId.ID_NUMBER_INT: case JsonTokenId.ID_NUMBER_FLOAT: return p.getDecimalValue(); case JsonTokenId.ID_STRING: String text = p.getText().trim(); // note: no need to call `coerce` as this is never primitive if (text == null || text.length() == 0) { return getNullValue(ctxt); } try { return new BigDecimal(text); } catch (IllegalArgumentException iae) { } return (BigDecimal) ctxt.handleWeirdStringValue(type, text, "not a valid representation"); case JsonTokenId.ID_START_ARRAY: throw new RuntimeException("NullToZeroDeserializer handleBigDecimal error, encounter token " + JsonTokenId.ID_START_ARRAY); } // Otherwise, no can do: return (BigDecimal) ctxt.handleUnexpectedToken(type, p); } @Override public Object getNullValue(DeserializationContext ctxt) throws JsonMappingException { if (this.type == Integer.class) { return 0; } if (this.type == BigDecimal.class) { return BigDecimal.ZERO; } return 0; }
待反序列化的bean:
@Data public class TestDeSerializerBean{ private Integer number; @JsonDeserialize(using = NullToZeroDeserializer.class) private BigDecimal money; private BigDecimal balance; }
反序列化Test:
@Test public void testDeSerializer() throws IOException { TestDeSerializerBean serializerBean = new TestDeSerializerBean(); serializerBean.setNumber(5); serializerBean.setMoney(new BigDecimal(20)); String string = mapper.writeValueAsString(serializerBean); String testStr = "{\n" + " \"number\": 5,\n" + " \"money\": \"20.0\"\n" + "}"; TestDeSerializerBean deSerializerBean = mapper.readValue(testStr, TestDeSerializerBean.class); System.out.println(deSerializerBean); }
测试结果:
null类型:
@Test public void testDeSerializer() throws IOException { TestDeSerializerBean serializerBean = new TestDeSerializerBean(); serializerBean.setNumber(5); serializerBean.setMoney(new BigDecimal(20)); String string = mapper.writeValueAsString(serializerBean); String testStr = "{\n" + " \"number\": 5,\n" + " \"money\": \"\"\n" + "}"; TestDeSerializerBean deSerializerBean = mapper.readValue(testStr, TestDeSerializerBean.class); System.out.println(deSerializerBean); }
2. 非null类型
反序列化的类序列化null值时,注意要重写 getNullValue方法
总结
以上只是针对null转0 以及0转null写的代码,当需要自定义的序列化时,往往可以参考已有的serializer 和deserializer类,譬如DateDeserializer和DateSerializer,BigDecimalDeserializer和BigDecimalSerializer。参考这些以后的序列化与反序列化类,我们可以写出任意想要的自定义的序列化和反序列化的效果
以上这篇jackson 实现null转0 以及0转null的示例代码就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持自学编程网。
- 本文固定链接: https://zxbcw.cn/post/196636/
- 转载请注明:必须在正文中标注并保留原文链接
- QQ群: PHP高手阵营官方总群(344148542)
- QQ群: Yii2.0开发(304864863)