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方法直接赋值,简直被自己蠢哭了。
学无止境,害得继续加油啊。