OGNL(Object-Graph Navigation Language)是一种表达式措辞,用于在Java运用程序中对工具图进行导航和操作。
OGNL本身并不供应直接的实行环境,它是作为一个库或框架的一部分来利用的。
因此,OGNL的实行办法取决于利用它的高下文。

一样平常情形下,OGNL可以通过两种办法实行:阐明实行和编译实行。

阐明实行:在阐明实行中,OGNL表达式在运行时逐条阐明和实行。
它会在每次表达式实行时动态打算表达式的结果,并根据工具图的实际状态进行导航和操作。
这种办法的灵巧性较高,可以根据须要对工具图进行动态操作,但相对而言实行效率较低。

php嵌套json聊聊多层嵌套的json的值若何解析/调换 React

编译实行:为了提高实行效率,有些框架会将OGNL表达式编译成可实行的字节码或类文件。
在编译实行中,OGNL表达式在编译阶段被转换成可实行代码,然后在运行时直接实行这些天生的代码。
这种办法可以在一定程度长进步实行速率,但捐躯了一些灵巧性,由于编译后的代码在运行时不再动态打算。

官网:https://commons.apache.org/proper/commons-ognl/language-guide.html

我们常常利用ORM框架mybatis的动态sql解析,它的实现基石便是OGNL表达式。
回到正题,我们如何利用OGNL来解析json

a、 在项目POM引入OGNL GAV

&lt;dependency> <groupId>ognl</groupId> <artifactId>ognl</artifactId> <version>${ognl.version}</version> </dependency>

b、 封装OGNL表达式工具类

public final class OgnlCache { private static final OgnlMemberAccess MEMBER_ACCESS = new OgnlMemberAccess(); private static final OgnlClassResolver CLASS_RESOLVER = new OgnlClassResolver(); private static final Map<String, Object> expressionCache = new ConcurrentHashMap<>(); private OgnlCache() { // Prevent Instantiation of Static Class } public static Object getValue(String expression, Object root) { try { Map context = Ognl.createDefaultContext(root, MEMBER_ACCESS, CLASS_RESOLVER, null); return Ognl.getValue(parseExpression(expression), context, root); } catch (OgnlException e) { throw new RuntimeException(&#34;Error evaluating expression '" + expression + "'. Cause: " + e, e); } } private static Object parseExpression(String expression) throws OgnlException { Object node = expressionCache.get(expression); if (node == null) { node = Ognl.parseExpression(expression); expressionCache.put(expression, node); } return node; }}

c、 封装json工具类

public final class JsonUtil { private JsonUtil(){} public static <T> T parse(String jsonStr, Class<T> clazz) throws Exception { return JSON.parseObject(jsonStr, clazz); } public static Object getValue(Map map, String path) throws Exception { return OgnlCache.getValue(path,map); }}

d、 多层嵌套json解析例子

private void printMenuI18nCodeByOgnl() throws Exception { String menuJson = mockMenuService.getMenuJson(); Map<String, Object> map = JsonUtil.parse(menuJson, Map.class); Object topMenu = JsonUtil.getValue( map,"i18NCode"); Object userMenu = JsonUtil.getValue( map,"children[0].i18NCode"); Object userMenuAdd = JsonUtil.getValue( map,"children[0].children[0].i18NCode"); Object userMenuUpdate = JsonUtil.getValue( map,"children[0].children[1].i18NCode"); Object deptMenu = JsonUtil.getValue( map,"children[1].i18NCode"); Object deptMenuList = JsonUtil.getValue( map,"children[1].children[0].i18NCode"); Object deptMenuDelete = JsonUtil.getValue( map,"children[1].children[1].i18NCode"); System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Print MenuI18nCode By Ognl Start <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<,"); System.out.println(topMenu); System.out.println(userMenu); System.out.println(userMenuAdd); System.out.println(userMenuUpdate); System.out.println(deptMenu); System.out.println(deptMenuList); System.out.println(deptMenuDelete); System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Print MenuI18nCode By Ognl End <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<,"); }

注: 示例中的menuJson形如下

{"children":[{"children":[{"children":[],"component":"saas/index","i18NCode":"user.menu.add","id":8,"linkUrl":"/user/add","menuName":"用户新增","parentId":9,"sort":9999},{"children":[],"component":"saas/index","i18NCode":"user.menu.update","id":7,"linkUrl":"/user/update","menuName":"用户编辑","parentId":9,"sort":9999}],"component":"saas/index","i18NCode":"user.menu","id":9,"linkUrl":"/user","menuName":"用户菜单","parentId":1,"sort":9999},{"children":[{"children":[],"component":"saas/index","i18NCode":"dept.menu.list","id":11,"linkUrl":"/dept/list","menuName":"部门列表","parentId":10,"sort":9999},{"children":[],"component":"saas/index","i18NCode":"dept.menu.delete","id":12,"linkUrl":"/dept/delete","menuName":"部门删除","parentId":10,"sort":9999}],"component":"saas/index","i18NCode":"dept.menu","id":10,"linkUrl":"/dept","menuName":"部门菜单","parentId":1,"sort":9999}],"component":"saas/index","i18NCode":"top.menu","id":1,"linkUrl":"/topUrl","menuName":"顶级菜单","parentId":0,"sort":9999}

解析后掌握台打印如下

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Print MenuI18nCode By Ognl Start <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<,top.menuuser.menuuser.menu.adduser.menu.updatedept.menudept.menu.listdept.menu.delete>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Print MenuI18nCode By Ognl End <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<,

OGNL表达式常用例子,可以查看如下链接

https://blog.51cto.com/rickcheung/238578

3、方法三:留个悬念,待会讲多层嵌套json更换1、方法一:循环遍历+正则进行更换

这种做法相对常规,且更换比较繁琐。

2、方法二:利用json类库,进行更换

以fastJSON为例

a、 在项目pom引入fastJSON GAV

<dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>${fastjson.version}</version> </dependency>

b、 多层嵌套json更换例子

以将菜单的i18nCode更换为详细措辞的值为例

public String reBuildMenuJson(){ String orginalMenuJson = getMenuJson(); JSONObject jsonObject = JSON.parseObject(orginalMenuJson); jsonObject.put(I18N_CODE_COLUMN,mockI18nCache.get(jsonObject.get(I18N_CODE_COLUMN))); reBuildChildJson(jsonObject); return JSON.toJSONString(jsonObject); } private void reBuildChildJson(JSONObject curentObject){ JSONArray children = curentObject.getJSONArray(CHILDREN_COLUMN); for (int i = 0; i < children.size(); i++) { JSONObject child = children.getJSONObject(i); child.put(I18N_CODE_COLUMN,mockI18nCache.get(child.get(I18N_CODE_COLUMN))); reBuildChildJson(child); } }

注: 未更换前,menuJson形如下

{"children":[{"children":[{"children":[],"component":"saas/index","i18NCode":"user.menu.add","id":8,"linkUrl":"/user/add","menuName":"用户新增","parentId":9,"sort":9999},{"children":[],"component":"saas/index","i18NCode":"user.menu.update","id":7,"linkUrl":"/user/update","menuName":"用户编辑","parentId":9,"sort":9999}],"component":"saas/index","i18NCode":"user.menu","id":9,"linkUrl":"/user","menuName":"用户菜单","parentId":1,"sort":9999},{"children":[{"children":[],"component":"saas/index","i18NCode":"dept.menu.list","id":11,"linkUrl":"/dept/list","menuName":"部门列表","parentId":10,"sort":9999},{"children":[],"component":"saas/index","i18NCode":"dept.menu.delete","id":12,"linkUrl":"/dept/delete","menuName":"部门删除","parentId":10,"sort":9999}],"component":"saas/index","i18NCode":"dept.menu","id":10,"linkUrl":"/dept","menuName":"部门菜单","parentId":1,"sort":9999}],"component":"saas/index","i18NCode":"top.menu","id":1,"linkUrl":"/topUrl","menuName":"顶级菜单","parentId":0,"sort":9999}

更换后,menuJson形如下

{"component":"saas/index","children":[{"component":"saas/index","children":[{"component":"saas/index","children":[],"linkUrl":"/user/add","menuName":"用户新增","id":8,"sort":9999,"i18NCode":"userMenuAdd","parentId":9},{"component":"saas/index","children":[],"linkUrl":"/user/update","menuName":"用户编辑","id":7,"sort":9999,"i18NCode":"userUpdateAdd","parentId":9}],"linkUrl":"/user","menuName":"用户菜单","id":9,"sort":9999,"i18NCode":"userMenu","parentId":1},{"component":"saas/index","children":[{"component":"saas/index","children":[],"linkUrl":"/dept/list","menuName":"部门列表","id":11,"sort":9999,"i18NCode":"deptMenuList","parentId":10},{"component":"saas/index","children":[],"linkUrl":"/dept/delete","menuName":"部门删除","id":12,"sort":9999,"i18NCode":"deptMenuDelete","parentId":10}],"linkUrl":"/dept","menuName":"部门菜单","id":10,"sort":9999,"i18NCode":"deptMenu","parentId":1}],"linkUrl":"/topUrl","menuName":"顶级菜单","id":1,"sort":9999,"i18NCode":"topMenu","parentId":0}3、方法三:利用json序列化表明

以菜单国际化为示例

1、自定义表明

@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)@JacksonAnnotationsInside@JsonSerialize(using = I18nJsonSerializer.class)public @interface I18nField {}

2、自定义国际化翻译接口(该详细实现留给业务扩展)

public interface I18nService { String getTargetContent(String i18nCode);}

题外话 : 为啥不像spring的messageSource定义成

String getMessage(String code, @Nullable Object[] args, @Nullable String defaultMessage, Locale locale);

由于很多参数信息可以直接通过高下文获取,比如Locale可以通过LocaleContextHolder.getLocale()

3、编写json序列化接口

public class I18nJsonSerializer extends JsonSerializer<String> implements ContextualSerializer { @Autowired private I18nService i18nService; @Override public void serialize(String s, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { jsonGenerator.writeString(i18nService.getTargetContent(s)); } @Override public JsonSerializer<?> createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty) throws JsonMappingException { I18nField i18nField = beanProperty.getAnnotation(I18nField.class); if(!ObjectUtils.isEmpty(i18nField) && String.class.isAssignableFrom(beanProperty.getType().getRawClass())){ return this; } return serializerProvider.findValueSerializer(beanProperty.getType(),beanProperty); }}

4、定义和json字段能够匹配的工具

大口语,便是json和这个工具可以相互转换。
以菜单为例

@Data@EqualsAndHashCode(callSuper = true, of = {"id"})public class MenuResourceDTO extends TreeDTO<MenuResourceDTO> implements Serializable { private static final long serialVersionUID = 1L; 。




省略其他属性 / 单菜名称 / private String menuName; private String permission; / 是否缓存 / private Integer keepAlive; @I18nField private String i18NCode; public static String I18N_CODE_COLUMN = "i18NCode"; public static String CHILDREN_COLUMN = "children";

5、在须要进行更换的字段上加上 @I18nField表明

@I18nField private String i18NCode;

6、更换验证

编写一个测试controller,用来输出更换后的菜单信息

@RestController@RequestMapping("menu")@RequiredArgsConstructorpublic class MockMenuController { private final MockMenuService mockMenuService; @GetMapping public MenuResourceDTO getMenu(){ return mockMenuService.getMenuResourceDTO(); }}

通过POSTMAN访问,得到如下信息

{ "id": 1, "parentId": 0, "sort": 9999, "children": [ { "id": 9, "parentId": 1, "sort": 9999, "children": [ { 。


省略其他信息 "menuName": "用户新增", "i18NCode": "userMenuAdd" }, { 。


省略其他信息 "menuName": "用户编辑", "i18NCode": "userUpdateAdd" } ], 。


省略其他信息 "menuName": "用户菜单", "i18NCode": "userMenu" }, "menuName": "顶级菜单", "i18NCode": "topMenu"}

回答上面多层json解析的方法三,那个悬念做法便是将json与工具映射起来,通过工具来取值

4、方法四:先自己发散下,然后看下总结总结

本文的多层嵌套json的解析和更换都供应了几种方案,综合来讲是推举将json先转工具,通过工具操作。
对json更换,推举利用自定义json序列化表明的办法。
但这种办法比较适宜json的构造以及字段是固定的办法。
对付低代码,本身的json构造是多种多样的,如果要后端实现,一种做法,便是将这些json都映射成工具,但由于json构造多种多样,就会导致要映射的工具膨胀。
另一种办法,是直接转JsonObject,通过JsonObject来操作更换

其次现在都是前后端分离,有些东西实在也可以放在前端实现,比如这种更换事情实在挺适宜放在前端做的。
以低代码为例,由于前端本来就须要解析json,后端可以掩护一个映射表,前端实现一个组件函数,通过该函数优先从前端缓存取,取不到再从调用后端接口,这便是json更换的方法四,把更换事情留给前端做,哈哈。
大家是一个团队,哪边好实现,就放哪边做

末了那个ognl的代码,我是直接把mybatis的源码搬过来,直接套用了。
开源有的东西,就没必要自己再搞一遍了

demo链接

https://github.com/lyb-geek/springboot-learning/tree/master/springboot-multinested-json-parse