本人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的值均为字符串类型,精度自然就不会丢失了。
总结:需求挺奇葩的,实现方式也挺奇葩的。如果哪位大神有理方便快捷的实现方式,欢迎留言!