假设有这样一段文本,里面混入了一些json字符串,我们需要把json字符串提取出来。网上大部分解决方案是正则表达式/\{.*?\}/
,但是正则的方法有很多局限性。比如下文这个例子,如果正则使用非贪婪模式,匹配会在"alice"后面的第一个“}”停下来,如果开启贪婪模式,则会把两个json当作一个。
Some text before {"key1":"value1","key2":{"alice":"alice"},"key3":{"bob":"bob"}} some text in between {"key4":"value4"} some text after.
我查了Google和ChatGPT,都没有一个现成的解决方案。看来只能发挥自己动手丰衣足食的精神了。
这个问题其实非常经典的栈数据结构的应用题,就是判断括号是否匹配的问题。如果前面有两个左括号,后面必须有两个右括号,才是完整的。那我们就实现一下。
/** * 提取json信息 * * @param content * @return */ protected static List<String> extractJson(String mixedContent) { List<String> result = new ArrayList<>(); Stack<Integer> stack = new Stack<>(); Boolean slash = false; for (int i = 0; i < mixedContent.length(); i++) { if (slash) { // 忽略所有的转义字符 slash = false; continue; } if (mixedContent.charAt(i) == '{') { stack.push(i); } else if (mixedContent.charAt(i) == '}') { if (stack.isEmpty()) { // System.out.println("没有对应的开括号,位置:" + i); continue; } int start = stack.pop(); String json = mixedContent.substring(start, i + 1); if (stack.size() == 0) { result.add(json); } } else if (mixedContent.charAt(i) == '\\') { slash = true; } } // if (!stack.isEmpty()) { // System.out.println("没有对应的闭括号,位置:" + stack.pop()); // } return result; }
因为我的场景都是json对象,都是从“{”开始的,因此我只处理了花括号{},至于花括号内部是否是一个正确的 json的问题,交给后续的 json 解析库去处理。
因为我只需要提取合法的部分,所以这里的两个报错信息被我注释掉了,如果需要严格的判断可以处理这两个错误信息。
同时简单处理了转义字符。因为他无关我们的闭合检查,所以直接忽略掉。
这段代码对我当前的需求算够用的,想完美一点,并且支持更多场景的话,可以继续补充完善,比如可以把[]和双引号也考虑进来,原理是一样的。