概要
之前有从边缘网关采集数据的需求,但是不同边缘网关上报格式不同,大部分是自定义Json模板进行采集,因此实现一个简单的Json解析工具类。主要用于特定设备上报Json模板,从中解析数据
实现思路
通过深度优先遍历到模板中$开头的字段,同时纪录路径,到json中递归寻找对应结果
实现代码
/**
* 使用深度优先搜索方法匹配模板和传入 JSON 的字段
* @param template 模板 JSON 对象或数组
* @param inputObj 传入 JSON 对象
* @param result 结果对象
* @param path 当前路径
*/
private static void matchFields(Object template, JSONObject inputObj, Map<String,Object> result, String path) {
if (template instanceof JSONObject) {
JSONObject templateObj = (JSONObject) template;
for (String templateKey : templateObj.keySet()) {
String currentPath = path.isEmpty() ? templateKey : path + "." + templateKey;
Object templateValue = templateObj.get(templateKey);
if (templateValue instanceof JSONObject || templateValue instanceof JSONArray) {
// 如果模板值是嵌套的 JSON 对象或数组,则继续递归匹配
matchFields(templateValue, inputObj, result, currentPath);
} else if (templateValue instanceof String && ((String) templateValue).startsWith("$")) {
// 如果模板值是 "$"开头,则提取对应的传入 JSON 字段的值
Object inputValue = getValueByPath(inputObj, currentPath);
// 去除$开头
result.put(((String) templateValue).substring(1), inputValue);
}
}
} else if (template instanceof JSONArray) {
JSONArray templateArray = (JSONArray) template;
for (int i = 0; i < templateArray.size(); i++) {
String currentPath = path + ".[" + i + "]";
Object templateElement = templateArray.get(i);
if (templateElement instanceof JSONObject || templateElement instanceof JSONArray) {
// 如果模板元素是嵌套的 JSON 对象或数组,则继续递归匹配
matchFields(templateElement, inputObj, result, currentPath);
} else if (templateElement instanceof String && templateElement.toString().startsWith("$")) {
// 如果模板元素是 "$"开头,则提取对应的传入 JSON 字段的值
Object inputValue = getValueByPath(inputObj, currentPath);
// 去除$开头
result.put(((String) templateElement).substring(1),inputValue);
}
}
}
}
深度优先搜索方法,不断递归同时记录当前路径,当遇到没有嵌套json同时开头为$时将值加入结果集
/**
* 根据路径获取 JSON 字段的值
* @param json 传入 JSON 对象
* @param path 路径
* @return JSON 字段的值
*/
private static Object getValueByPath(JSONObject json, String path) {
String[] keys = path.split("\\.");
Object currentObj = json;
// 遍历路径
for (String key : keys) {
if (currentObj instanceof JSONObject) {
JSONObject jsonObject = (JSONObject) currentObj;
if (jsonObject.containsKey(key)) {
currentObj = jsonObject.get(key);
} else {
// 如果路径中的字段不存在,则返回 null 或抛出异常,根据需要进行处理
return null;
}
} else if (currentObj instanceof JSONArray) {
JSONArray jsonArray = (JSONArray) currentObj;
int index = getIndexFromKey(key);
if (index >= 0 && index < jsonArray.size()) {
currentObj = jsonArray.get(index);
} else {
// 如果索引越界,则返回 null 或抛出异常,根据需要进行处理
return null;
}
} else {
// 当前路径无法继续匹配
return null;
}
}
return currentObj;
}
获取实际结果值方法,传入json和路径获取值
/**
* 从路径中获取索引
* @param key 传入路径中的字段名
* @return 索引
*/
private static int getIndexFromKey(String key) {
// 获取路径中的索引
try {
// 去除索引括号
key = key.replaceAll("[\\[\\]]", "");
return Integer.parseInt(key);
} catch (NumberFormatException e) {
return -1;
}
}
获取索引方法,判断索引位置
使用示例
public static void main(String[] args) {
// 示例:假设一个名为 template 的模板 JSON 对象或数组,以及一个名为 inputJson 的传入 JSON 字符串
String templateJson = "{\"a\": {\"b\": {\"c\": \"$value1\"}}, \"d\": [{\"e\": \"$value2\"}]}";
String inputJson = "{\"a\": {\"b\": {\"c\": 123}}, \"d\": [{\"e\": \"hello\"}, {\"e\": \"world\"}]}";
// 解析模板和传入的 JSON 字符串
Object template = parseTemplate(templateJson);
JSONObject inputObj = JSONObject.parseObject(inputJson);
// 创建结果对象
Map<String,Object> result = new JSONObject();
// 使用深度优先搜索方法匹配模板和传入 JSON 的字段
matchFields(template, inputObj, result, "");
// 打印结果
System.out.println(result);
}
对于边缘网关中,可以把结果集的Key定义为属性名,Value定义为具体数值,在实际业务中通过Key属性名关联设备属性等数据,同时存储采集值
总结
这篇文章仅记录一个简单的模板解析器,更快的实现部分功能。更复杂的解析暂不支持。