jackson 实现null转0 以及0转null的示例代码

需求背景

最近遇到一个需求,有个数值类型的字段,非必填,默认为空,数据库表针对该字段设计的是一个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的示例代码就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • jackson 实体转json 为NULL或者为空不参加序列化(实例讲解)

    使用jackson进行序列化时,往往会遇到后台某个实体对象的属性为null,当序列化成json时对应的属性也为null:这样在某些前端组件上应用该json对象会报错.(例如:echarts) 下面总结了两种方法,解决了当属性为null时不参与序列化: 方法一: 1.实体上使用如下注解 @JsonInclude(Include.NON_NULL) 将该标记放在属性上,如果该属性为NULL则不参与序列化 :如果放在类上边,那对这个类的全部属性起作用. 具体取值有: //Include.Include

  • jackson设置返回null为空字符串的操作

    1.将接口返回的字段为null的设置为空字符串: 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 org.s

  • Android 7.0 监听网络变化的示例代码

    Android7.0前,Android系统前网络切换时,会发广播,业务只要监听广播即可. public class NetChangeReceiver extends BroadcastReceiver { private static final String ANDROID_NET_CHANGE_ACTION = "android.net.conn.CONNECTIVITY_CHANGE"; @Override public void onReceive(Context conte

  • Spring 5.0集成log4j2日志管理的示例代码

    在使用Spring框架的时候,我们可以很方便的配置log4j来进行日志管理. Spring 5.0发布一段时间了,最近将项目从Spring 4.3升级到Spring 5.0,Spring 4.3集成log4j所用的类org.springframework.web.util.Log4jConfigListener在Spring 5.0版本已经删除,而且log4j 1.x版已经不再更新.我们将log4j-1.x升级为log4j-2.x 先引入log4j 2的三个jar包 log4j-api-2.10

  • yii2.0整合阿里云oss的示例代码

    主要思路,首先用composer下载阿里云oss的php sdk,然后自定义一个组件,就能够在全局调用阿里云oss了. 具体步骤如下: 1 到阿里云官网找到php sdk,网址为https://help.aliyun.com/document_detail/32099.html?spm=5176.87240.400427.40.s8EbEH 2 使用composer安装,在项目目录下(basic/)输入: composer require aliyuncs/oss-sdk-php 安装完成以后,

  • jackson 实现null转0 以及0转null的示例代码

    需求背景 最近遇到一个需求,有个数值类型的字段,非必填,默认为空,数据库表针对该字段设计的是一个int类型, 由于dba推荐规范,默认该值是not null.这个时候,问题就来了,数据库默认存的是0,前端展示时,又不能显示这个0(需要的是null) 解决方案 针对此类处理,通常的方案有以下2种: 前端做处理,统一对0和null做处理,0即是null,null即是0 后端做处理,针对要处理的字段,在序列化之前或者之后做处理,或者采取硬编码的方式,针对要处理的字段,写if else 方案分析 针对第

  • JavaScript中为什么null==0为false而null大于=0为true(个人研究)

    生活中我们在不停的编写代码,写着JavaScript,很少有时间进行概念上的研究.我呢,今天闲来没啥事,研究了一下有关"null"和"0"的关系.希望大家看完了能有所收获. 复制代码 代码如下: alert(null>=0) 复制代码 代码如下: 上面的代码会弹出什么来呢?False?True?其实是true.那么是为什么呢?为什么"null>=0"为true呢?null>=0的时候,强转为数字类型.在进行null>=0比

  • C语言中0、‘\0‘、‘0‘、NULL以及类型转化

    0 '\0' '0' NULL 0 '\0' NULL都是三种0值,它们在数字上是完全一样的,而且在内存中存的都是二进制0. 所以,它们的值是一样的,只不过表现的形式不一样,也就是它们的类型是不同的: 0在整形中表示数字0,在字符中'0'表示一个字符它的ASCII码值为48, '\0'是一个字符,表示字符串结束,在ASCII中的值为0 NULL 即空指针,它表示一个指针指向地址为0的空间,可以看到这里的0被强制转化为void*指针,这也就意味着而NULL可以赋值给任何类型的指针: 对于不同的类型

  • mysql count 为null时,显示0的问题

    目录 mysql count 为null时,显示0 1.使用ifnull 2.运行结果 mysql让count为0的记录也显示出来 在mysql 下执行如下命令 mysql count 为null时,显示0 1.使用ifnull ifnull(字段名,目标值) SELECT a.*,IFNULL(r.count,0) from (SELECT act_id,poster_id,type,style_type,status,status_time,title,content,images,star

  • ASP.NET Core 2.0 支付宝扫码支付的实现代码

    前言 自从微软更换了CEO以后,微软的战略方向有了相当大的变化,不再是那么封闭,开源了许多东西,拥抱开源社区,.NET实现跨平台,收购xamarin并免费提供给开发者等等.我本人是很喜欢.net的,并希望.net core能够崛起.我是从.net core 1.1的时候开始使用的,到现在的.net core 2.0..net core 2.0比1.1有了一些改变,api也增加了很多,用着更顺手了,最近在做asp.net core 对接支付宝,百度了一下没找到关于core的支付宝支付相关资料,所以

  • vue2.0在table中实现全选和反选的示例代码

    其实在去年小颖已经写过一篇:Vue.js实现checkbox的全选和反选 小颖今天在跟着慕课网学习vue的过程中,顺便试试如何在table中实现全选和反选,页面的css样式是直接参考慕课网的样式写的,js是小颖自己写哒,欢迎大家吐槽和点赞,嘻嘻 demo的  git 地址:ShoppingCart 页面效果: 具体怎么实现的呢? 使用localstorage来存储页面信息中已经有写项目是怎么创建的所以小颖在这里就不重复了,其实只是在上篇文章的基础上稍微做了改动: App.vue文件 <temp

  • Spring Security Oauth2.0 实现短信验证码登录示例

    本文介绍了Spring Security Oauth2.0 实现短信验证码登录示例,分享给大家,具体如下: 定义手机号登录令牌 /** * @author lengleng * @date 2018/1/9 * 手机号登录令牌 */ public class MobileAuthenticationToken extends AbstractAuthenticationToken { private static final long serialVersionUID = SpringSecur

随机推荐