背景
新版本代码重构,要对下游上报的数据进行Json格式化之后落盘。联调的时候发现如果上报数据里面含有特殊字符,这个时候是Json是解析不了的, 要进行特殊处理之后再格式化落盘。
代码实现
public static String formatJson(String str) {
Object parse;
try {
// 先解析一遍,如果解析成功说明不包含特殊字符,直接进行格式化处理
// 这样的好处是避免对普通json数据进行特殊处理,减少不必要的处理
parse = JSON.parse(str);
} catch (Exception e) {
// 解析失败,可能含有特殊字符,特殊处理之后再尝试解析
// 如果还解析失败,那么原数据有问题,直接抛出异常,方法最外层捕获
str = replaceSpecial(str);
parse = JSON.parse(str);
}
// 数组格式化
if (parse instanceof JSONArray) {
return JSON.toJSONString(JSONArray.parseArray(str), SerializerFeature.PrettyFormat);
// 对象格式化
} else if (parse instanceof JSONObject) {
return JSON.toJSONString(JSONObject.parseObject(str), SerializerFeature.PrettyFormat);
} else {
// 两种都不是就返回原数据
return str;
}
}
/**
* 特殊数据处理,特殊字符不转义,单个“\”进行处理
* @param str 原数据
* @return 处理之后的数据
*/
public static String replaceSpecial(String str) {
List<String> specialList = new ArrayList<>();
Collections.addAll(specialList,"a","b","f","n","t","v","r","s","w","0","?","'","\"");
char[] charArray = str.toCharArray();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < charArray.length; i++) {
if ("\\".equals(String.valueOf(charArray[i]))) {
// 下一个字符是特殊字符就不处理
if (specialList.contains(String.valueOf(charArray[i+1]))) {
sb.append(charArray[i]);
continue;
}
// 如果连续的多个\就要做特殊转义处理,偶数斜杠不处理,单数斜杠处理
if (getSlashNumberBefore(sb.toString(), i)%2!=0) {
sb.append(charArray[i]).append(charArray[i]);
} else {
sb.append(charArray[i]);
}
} else {
sb.append(charArray[i]);
}
}
return sb.toString();
}
private static int getSlashNumberBefore(String beforeStr, int i) {
if (beforeStr.length() < i) {
return i;
}
if ("\\".equals(beforeStr.substring(beforeStr.length() -i, beforeStr.length() -i+1))) {
i++;
i = getSlashNumberBefore(beforeStr, i);
}
return i;
}