web项目中json字符串处理时double数据精度丢失的问题解决方案

本人Java,在一个WEB项目开发过程中与C++联调时遇到一个问题,我这边的json字符串中,value数据都是字符串形式的,C++无法识别,要求我们将其中的value转换成实际格式后传过去。

具体要求是:
(1)如果是字符串,就必须加上双引号;
(2)如果是整数,就不能加上双引号,且数字不能有小数点;
(3)如果是小数,不加双引号,且必须有小数点。如:数字1.0,不能去掉小数点,传1就会导致程序异常。

前面两点其实没有任何实现难度,但是第三点在开发过程中却遇到了问题。

众所周知,前端JS是没有1.0这种小数存在的,只要将1.0传给前端,前端拿到后就自动变成1。JS是弱类型语言,将字符串“1.1”直接与1相乘可以直接得到数字1.1,而将字符串“1.0”与1相乘或者与1.0相乘,得到的却是1,不可能是1.0。基于以上,直接通过前端将数据1.0这种数字传到后端是不可能实现的,只有通过其它办法曲线救国了。

要处理好这个数据转换的问题,主要有数据保存和数据回显两方面要考虑。

第一方面,数据保存。JS不存在数字1.0,那就只能通过字符串“1.0”将数据传到后端了。那么,这个数据到底是什么类型?字符串,整数还是小数?只能增加一个标识告诉后端了。前端传给后端的数据是一个json字符串,暂且将标识追加在key上面,外层无需标识,只有最里层才有具体数值,将标识加在最里层的key上就可以了。

约定:若数据为整型,则在其后增加->int;若数据为浮点型,则在其后增加->double;数据为字符串类型,则在其后增加->string。

若原json为如下所示:

								{
								    "status1": {
								        "status2": "1"
								    },
								    "status3": 1.0,
								    "status4": 1
								}

则前端需要处理成如下格式再传给后端:

								 {
								    "status1": {
								        "status2->string": "1"
								    },
								    "status3->double": "1.0",
								    "status4->int": "1"
								}

后端在接收到前端转换后的json字符串后,将其转换成正常的json,将正常json存储下来,代码如下:

	public static Map<String, Object> convertWebJson(Map<String, Object> json){
	    //由于涉及到集合中元素的删除修改操作,直接遍历Map的EntrySet会产生ConcurrentModificationException异常
        List<Map.Entry<String, Object>> entryList = new ArrayList<>(json.entrySet());
        for(Map.Entry<String, Object> entry : entryList){
            String key = entry.getKey();
            Object value = entry.getValue();
            if(value instanceof JSONObject){
                convertWebJson((JSONObject)value);
            }else{
                String valueStr = value.toString().trim();
                if(key.contains("->")){
                    String[] keys = key.split("->");
                    String type = keys[1];
                    if("int".equalsIgnoreCase(type)){
                        if(valueStr.contains(".")){
                            valueStr = valueStr.substring(0, valueStr.indexOf("."));
                        }
                        json.put(keys[0], Integer.parseInt(valueStr));
                    }else if("double".equalsIgnoreCase(type)){
                        json.put(keys[0], Double.parseDouble(valueStr));
                    }else if("string".equalsIgnoreCase(type)){
                        json.put(keys[0], valueStr);
                    }
                    json.remove(key);
                }
            }
        }
        return json;
    }

测试代码:

  public static void main(String[] args) {
      String webJsonStr = "{\"status1\":{\"status2->string\":\"1\"},\"status3->double\":\"1.0\",\"status4->int\":\"1\"}";
      String serverJsonStr = convertWebJson(JSON.parseObject(webJsonStr)).toString();
      System.out.println(serverJsonStr);
  }

打印结果:

	{"status1":{"status2":"1"},"status3":1.0,"status4":1}

另一方面,数据回显。如果直接将上述数据传给前端,显然数字1.0到达前端后直接就变成了1。当然,前端就算能完整地拿到这个json串,也不方便回显展示,将数据转换为对象数组传给前端,前端拿到的一个个对象均为“key-value-children”格式,如此即方便回显展示,也方便将修改后的新数据转换为json字符串再传给后端。json转对象数组,我在本站 JSON对象转换为JAVA对象(key-value-children) 这篇文章中有详细说明。此文中列出的json字符串中value值均看作是字符串类型,返回至前端自然也不会丢失精度。
而当json字符串中value值为浮点类型的时候,想要精度不丢失,就只有一个办法了,将浮点类型数据直接转换为字符串,但额外在对象中增加一个字段,标识字段的类型即可。转换代码在JSON对象转换为JAVA对象(key-value-children) 这篇文章基础上稍作修改:

	public static List<Object> convertJson2KvObj(JSONObject json){
        List<Object> objList = new ArrayList<>();
        for(Map.Entry<String, Object> entry : json.entrySet()){
            KeyValueObj obj = new KeyValueObj();
            obj.setKey(entry.getKey());
            Object value = entry.getValue();
            if(entry.getValue() instanceof JSONObject){
                obj.setChildren(convertJson2KvObj((JSONObject)entry.getValue()));
            }else if(entry.getValue() instanceof JSONArray){
                List<Object> list = new ArrayList<>();
                for (Object o : (JSONArray) entry.getValue()) {
                    list.add(convertJson2KvObj((JSONObject) o));
                    obj.setChildren(list);
                }
            }else{
                obj.setValue(entry.getValue() == null ? null : entry.getValue().toString());
                //根据value数据类型设置dataType
                // 1:整型   2:浮点型  3:字符串
                if(value instanceof Integer){
                    obj.setDataType(1);
                }else if(value instanceof BigDecimal){
                    obj.setDataType(2);
                }else if(value instanceof String){
                    obj.setDataType(3);
                }
            }
            objList.add(obj);
        }
        return objList;
    }

实体类中增加dataType字段:

import lombok.Data;
import java.util.List;

@Data
public class KeyValueObj {

    private String key;
    private String value;
    private Integer dataType;
    private List<Object> children;
}

测试代码:

	public static void main(String[] args) {
        String serverJsonStr = "{\"status1\":{\"status2\":\"1\"},\"status3\":1.0,\"status4\":1}";
        List<Object> dataList = convertJson2KvObj(JSON.parseObject(serverJsonStr));
        System.out.println(dataList);
    }

打印结果(整理后):

	[KeyValueObj(key=status1, value=null, dataType=null, 
					children=[KeyValueObj(key=status2, value=1, dataType=3, children=null)]
				), 
	KeyValueObj(key=status3, value=1.0, dataType=2, children=null), 
	KeyValueObj(key=status4, value=1, dataType=1, children=null)]

在上述结果中,value的值均为字符串类型,精度自然就不会丢失了。
总结:需求挺奇葩的,实现方式也挺奇葩的。如果哪位大神有理方便快捷的实现方式,欢迎留言!

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值