SpringMVC中json数据映射为java对象的坑


  在开发过程中,我们对外提供的服务可以抽成两大块,一块是不变的,即我们抽象出来的请求头,一块是根据不同的接口而不同的请求体。

package com.bijian.study.dto;

public class BaseRequest<T> {
    
    private CommonReqHeaderDTO commonReqHeaderDTO;
    private T data;
    
    public CommonReqHeaderDTO getCommonReqHeaderDTO() {
        return commonReqHeaderDTO;
    }
    public void setCommonReqHeaderDTO(CommonReqHeaderDTO commonReqHeaderDTO) {
        this.commonReqHeaderDTO = commonReqHeaderDTO;
    }
    public T getData() {
        return data;
    }
    public void setData(T data) {
        this.data = data;
    }
}

  而我们在测试过程发现一个小小的问题,如果对方传的json串都是字符串类型的,则会原封不动地映射到我们的java对象上。但如果对方有传非字符串的值,如25.60,则到映射到对象的值则为25.6,由于我们是面向公网的服务,会对请求过来的数据进行验签,如请求方验签的是25.60,而我们验签的值为成了25.6,则会验签不通过。所以,我特意分析了一下。记录如下:

1.不管是数值还是非数值的的,让请求方统一按字符串传。

2.在我们的系统,每一个请求体都定义一个对象,如这里的BatchOper.java

@ResponseBody
@RequestMapping(value="/addUser", method=RequestMethod.POST)
public BaseResponse addUser(HttpServletRequest request, HttpServletResponse response, @RequestBody BaseRequest<BatchOper> baseRequest) {
    logger.info("第三方请求,请求参数:{}", JsonUtil.toFullJson(baseRequest));
    return null;
}
package com.bijian.study.dto;

import java.math.BigDecimal;

import com.bijian.study.mapper.CustomBigDecimalSerialize;
import com.bijian.study.mapper.CustomDoubleSerialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;

public class BatchOper {

    //批次交易订单号
    private String batchOrderId;
    //汇总笔数
    private int totalCount;
    //汇总金额
    private BigDecimal totalAmount;
    
    private Double totalAmount2;
    
    private BigDecimal totalAmount3;
    //文件名称
    private String fileName;
    
    public String getBatchOrderId() {
        return batchOrderId;
    }
    public void setBatchOrderId(String batchOrderId) {
        this.batchOrderId = batchOrderId;
    }
    public int getTotalCount() {
        return totalCount;
    }
    public void setTotalCount(int totalCount) {
        this.totalCount = totalCount;
    }
    public BigDecimal getTotalAmount() {
        return totalAmount;
    }
    public void setTotalAmount(BigDecimal totalAmount) {
        this.totalAmount = totalAmount;
    }
    public Double getTotalAmount2() {
        return totalAmount2;
    }
    public void setTotalAmount2(Double totalAmount2) {
        this.totalAmount2 = totalAmount2;
    }
    public BigDecimal getTotalAmount3() {
        return totalAmount3;
    }
    public void setTotalAmount3(BigDecimal totalAmount3) {
        this.totalAmount3 = totalAmount3;
    }
    public String getFileName() {
        return fileName;
    }
    public void setFileName(String fileName) {
        this.fileName = fileName;
    }
}

请求的JSON串如下:

{"commonReqHeaderDTO":{"headReqDate":"20180208","headReqTime":"01241401978"},"data":{"batchOrderId":"0417775230004","totalCount":5,"totalAmount":"252.60","totalAmount2":25.80,"totalAmount3":125.50,"fileName":"/20180417/cs-gdgthzz041702.txt"}}

输出日志如下:

21:13:14.044 INFO  com.bijian.study.controller.UserController 67 addUser - 第三方请求,请求参数:{"commonReqHeaderDTO":{"headReqDate":"20180208","headReqTime":"01241401978"},"data":{"batchOrderId":"0417775230004","totalCount":5,"totalAmount":252.60,"totalAmount2":25.8,"totalAmount3":125.50,"fileName":"/20180417/cs-gdgthzz041702.txt"}}

      我们不难发现,BigDecimal和String类型的末尾的0还是保留,即正常映射了,但Double类型的则丢掉了最后的0。

      因此,方法二就是每一个请求体都定义一个映射对象,数字类型的可以为String,也可以是BigDecimal。

     但,对于Double,我们是否还有其它方法呢?这时,我们不难想到SpringMVC的自定义转换器。

package com.bijian.study.mapper;

import java.io.IOException;
import java.text.DecimalFormat;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;

public class CustomDoubleSerialize extends JsonSerializer<Double> {

    private DecimalFormat df = new DecimalFormat("##.0000");
    
    @Override
    public void serialize(Double value, JsonGenerator gen, SerializerProvider serializers)
            throws IOException, JsonProcessingException {
        
        if(value != null) {
            //gen.writeString(df.format(value));//转换成字符串
            gen.writeNumber(df.format(value));
        }
    }
}
package com.bijian.study.mapper;

import java.io.IOException;
import java.math.BigDecimal;
import java.text.DecimalFormat;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;

public class CustomBigDecimalSerialize extends JsonSerializer<BigDecimal> {

    private DecimalFormat df = new DecimalFormat("##.000");
    
    @Override
    public void serialize(BigDecimal value, JsonGenerator gen, SerializerProvider serializers)
            throws IOException, JsonProcessingException {
        
        if(value != null) {
            //gen.writeString(df.format(value));
            gen.writeNumber(df.format(value));
        }
    }
}

  然后需要给需要转换的属性上面加上注解,如下在totalAmount、totalAmount2分别加上自定义转换器。

package com.bijian.study.dto;

import java.math.BigDecimal;

import com.bijian.study.mapper.CustomBigDecimalSerialize;
import com.bijian.study.mapper.CustomDoubleSerialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;

public class BatchOper {

    //批次交易订单号
    private String batchOrderId;
    //汇总笔数
    private int totalCount;
    //汇总金额
    @JsonSerialize(using=CustomBigDecimalSerialize.class)
    private BigDecimal totalAmount;
    
    @JsonSerialize(using=CustomDoubleSerialize.class)
    private Double totalAmount2;
    
    private BigDecimal totalAmount3;
    //文件名称
    private String fileName;
    
    public String getBatchOrderId() {
        return batchOrderId;
    }
    public void setBatchOrderId(String batchOrderId) {
        this.batchOrderId = batchOrderId;
    }
    public int getTotalCount() {
        return totalCount;
    }
    public void setTotalCount(int totalCount) {
        this.totalCount = totalCount;
    }
    public BigDecimal getTotalAmount() {
        return totalAmount;
    }
    public void setTotalAmount(BigDecimal totalAmount) {
        this.totalAmount = totalAmount;
    }
    public Double getTotalAmount2() {
        return totalAmount2;
    }
    public void setTotalAmount2(Double totalAmount2) {
        this.totalAmount2 = totalAmount2;
    }
    public BigDecimal getTotalAmount3() {
        return totalAmount3;
    }
    public void setTotalAmount3(BigDecimal totalAmount3) {
        this.totalAmount3 = totalAmount3;
    }
    public String getFileName() {
        return fileName;
    }
    public void setFileName(String fileName) {
        this.fileName = fileName;
    }
}

这时,我们同样用上面的json串请求,输出日志如下:

21:20:28.512 INFO  com.bijian.study.controller.UserController 67 addUser - 第三方请求,请求参数:{"commonReqHeaderDTO":{"headReqDate":"20180208","headReqTime":"01241401978"},"data":{"batchOrderId":"0417775230004","totalCount":5,"totalAmount":252.600,"totalAmount2":25.8000,"totalAmount3":125.50,"fileName":"/20180417/cs-gdgthzz041702.txt"}}

  但,这种方式在我们的项目中还是不可取,因为不可能把所有请求体都定义出来,这样就达不到我们原来特意做成抽象类的目的了。最后我的方法是在验签前,对JSON串中的有小数点的数字类型的小数做补0处理。方法如下所示:

jsonStr = jsonStr.replaceAll("\\.0,", ".00,");
jsonStr = jsonStr.replaceAll("\\.0}", ".00}");

jsonStr = jsonStr.replaceAll("\\.1,", ".10,");
jsonStr = jsonStr.replaceAll("\\.1}", ".10}");

jsonStr = jsonStr.replaceAll("\\.2,", ".20,");
jsonStr = jsonStr.replaceAll("\\.2}", ".20}");

jsonStr = jsonStr.replaceAll("\\.3,", ".30,");
jsonStr = jsonStr.replaceAll("\\.3}", ".30}");

jsonStr = jsonStr.replaceAll("\\.4,", ".40,");
jsonStr = jsonStr.replaceAll("\\.4}", ".40}");

jsonStr = jsonStr.replaceAll("\\.5,", ".50,");
jsonStr = jsonStr.replaceAll("\\.5}", ".50}");

jsonStr = jsonStr.replaceAll("\\.6,", ".60,");
jsonStr = jsonStr.replaceAll("\\.6}", ".60}");

jsonStr = jsonStr.replaceAll("\\.7,", ".70,");
jsonStr = jsonStr.replaceAll("\\.7}", ".70}");

jsonStr = jsonStr.replaceAll("\\.8,", ".80,");
jsonStr = jsonStr.replaceAll("\\.8}", ".80}");

jsonStr = jsonStr.replaceAll("\\.9,", ".90,");
jsonStr = jsonStr.replaceAll("\\.9}", ".90}");

 

参考文章:https://blog.csdn.net/u013220534/article/details/79697554

测试的完整代码可到http://bijian1013.iteye.com/blog/2419741下载。


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM