Json和结构体间相互转换
这几个星期一直很忙,这篇文章本来是打算上周末发的,结果拖到了现在。
Java web中遇见把调用C库时,入参和出参的结构体转换成Json格式输出。有的结构体内含有大量属性的时候,一个一个添加手都要加断了。于是我使用了JAVA的反射机制来实现结构体和JSON格式之间的相互转换。先贴代码:
/** * @apiNot Json转结构体 */ public static Structure Json2Struct(JSONObject jsonObject, Structure structure){ Map<String, Object> map = jsonObject; //获取结构体字段 Field[] fields = structure.getClass().getDeclaredFields(); int e = 0; //遍历赋值 for (Map.Entry<String, Object> entry : map.entrySet()){ int find = 0; for(Field field : fields){ // 对于每个属性,获取属性名 String varName = field.getName(); //过滤掉结构体属性名中的前缀 varName = varName.replace("sz","").replace("ul",""); //给每个结构体中的成员修改权限,使其可以人为修改其成员值(private改为public) boolean access = field.isAccessible(); if(!access) field.setAccessible(true); //找到和jsonde的key值相同的成员名称并赋值 if(varName.equals(entry.getKey())) { try { Object object = field.get(structure); //判断结构体中成员类型(依自己情况而定) if("byte[]".equals(object.getClass().getTypeName())) { field.set(structure, entry.getValue().toString().getBytes()); } else { field.set(structure, entry.getValue()); } }catch (Exception ex){ ex.printStackTrace(); } find = 1; e++; } } if(1 != find) { SDKLogger.PrintDebug(SDKConst.loglevel.ERROR, "该结构体类型中没有Json中的" + entry.getKey() + "变量"); } } if(0 == e) { SDKLogger.PrintDebug(SDKConst.loglevel.ERROR, "该结构体类型与Json不匹配"); } return structure; } json转结构体主要注意不同的结构体成员类型的赋值方式不一样。
结构体转Json相对要简单一些,不需要Json入参,便利每个结构体属性再把他们的成员名和值一个一个构建成json就可以了:
/** * @apiNot 结构体转Json */ public static JSONObject Struct2Json(Structure structure){ JSONObject jsonObject = new JSONObject(); // 获取对象obj的所有属性域 Field[] fields = structure.getClass().getDeclaredFields(); for (Field field : fields) { // 对于每个属性,获取属性名 String varName = field.getName(); //过滤掉属性名中的前缀 varName = varName.replace("sz","").replace("ul",""); try { boolean access = field.isAccessible(); if(!access) field.setAccessible(true); //从obj中获取field变量 Object obj = field.get(structure); if("byte[]".equals(obj.getClass().getTypeName())) { jsonObject.put(varName, new String((byte[])obj)); } else { jsonObject.put(varName, obj); } if(!access) field.setAccessible(false); } catch (Exception ex) { ex.printStackTrace(); } } return jsonObject; } 注:代码注释中去掉属性名中的前缀是因为公司restful规范和C语言编程规范做的特殊处理。还有就是在结构体中包含的结构体无法转换成Json,反过来也是。这个问题暂时没有进行优化,待后续改进。
如果有哪位过客有什么修改意见,欢迎提出来哈,不,一定要提出来哈。
———————————————————————————————————————————————————
更新一下,上面的代码bug比较多,哈哈,下面这个是经过后来修改后已经用了很久的代码,贴出来对比一下。
/** * @apiNot Json转结构体 */ public static Structure Json2Struct(JSONObject jsonObject, Structure structure){ Map<String, Object> map = jsonObject; //获取结构体字段 Field[] fields = structure.getClass().getDeclaredFields(); int e = 0; //遍历赋值 for (Map.Entry<String, Object> entry : map.entrySet()){ int find = 0; for(Field field : fields){ // 对于每个属性,获取属性名 String varName = field.getName(); //过滤掉结构体属性名中的前缀(这是我自己写的方法) varName = StringHandler.removLowerHaed(varName); boolean access = field.isAccessible(); if(!access) field.setAccessible(true); if(varName.equals(entry.getKey())) { try { Object object = field.get(structure); if("byte[]".equals(object.getClass().getTypeName()) || object instanceof byte[]) { //字符串拷贝,也是我自己封装的一个方法,添加了中文编码支持 StringHandler.ArrayCopy((byte[])object, entry.getValue().toString().getBytes("utf-8")); } //结构体中包含结构体时的情况 else if(object.getClass().getName().contains("Structure")) { //出现数组的情况 if(object.getClass().isArray()) { JSONArray array = (JSONArray)entry.getValue(); int length = Array.getLength(object); for(int i=0;i<length;i++) { Json2Struct((JSONObject)array.get(i), (Structure)Array.get(object,i)); } } else { Json2Struct((JSONObject)entry.getValue(), (Structure)object); } } else { field.set(structure, entry.getValue()); } }catch (Exception ex){ ex.printStackTrace(); } find = 1; e++; } } if(1 != find) { logger.debug("该结构体类型中没有Json中的" + entry.getKey() + "变量"); } } if(0 == e) { logger.error("该结构体类型与Json不匹配"); } return structure; } /** * @apiNot 结构体转Json */ public static JSONObject Struct2Json(Structure structure){ JSONObject jsonObject = new JSONObject(); // 获取对象obj的所有属性域 Field[] fields = structure.getClass().getDeclaredFields(); for (Field field : fields) { // 对于每个属性,获取属性名 String varName = field.getName(); //过滤掉属性名中的前缀(这个是根据需求加的) varName = StringHandler.removLowerHaed(varName); if(varName.equals("Reserv")){ //预留字段不必传给客户端(我们的C++代码中很多结构体会留有预留字段) continue; } try { //打开修改权限 boolean access = field.isAccessible(); if(!access) field.setAccessible(true); //从obj中获取field变量 Object obj = field.get(structure); if("byte[]".equals(obj.getClass().getTypeName())) { String str = StringHandler.bytesToString((byte[])obj); jsonObject.put(varName, str); } else if(obj.getClass().getName().contains("Structure")) { if(obj.getClass().isArray()) { JSONArray array = new JSONArray(); int length = Array.getLength(obj); for(int i=0;i<length;i++) { array.add(i, Struct2Json((Structure)Array.get(obj,i))); } jsonObject.put(varName, array); } else { jsonObject.put(varName, Struct2Json((Structure)obj)); } } else { jsonObject.put(varName, obj); } } catch (Exception ex) { ex.printStackTrace(); } } return jsonObject; } 可以看出来,这段代码和之前的比,添加了处理结构体中包含结构体的状况,还有添加了中文编码。这些事后来遇到问题时修改的。前一段代码中还有个大bug就是在遇到byte[]数组的时候我竟然用了put方法直接赋值,简直被自己蠢哭了。
学无止境,害得继续加油啊。