与公司后端对接的时候erlang工程师经常丢过来这样一串字符串
{{1,7,[1,[2,3]]},3}"
或是这样一串
[{1,7,[1,[2,3]]},3]"
有点类似json又不是json,因为括号会出现多次也不适合用正则解析。这种情况怎么办呢?
解决方案如下:
1.观察规律:
每个括号是一对对存在 ’[‘对应‘]’,表示包含一个数组,‘{’对应'}',表示包含一个类,最前面的括号对应最后面的括号,考虑使用栈进行括号匹配
类或数组没有明确指明字段名,也可能出现 有字段名的(暂时无此需求,本文代码只针对无字段名形式的,后续有遇到会进行文章更新),定义一个erlangObject的类,可以往里面存数组或者类,若无指定字段则从1开始做为编号做属性名
2.代码实现:
先定义ErlangObject类,这里我使用了一个map保存属性,假设一个object是一个map,属性名是map的key,属性值就是key对应的值
import com.alibaba.fastjson.JSONException;
import java.util.HashMap;
import java.util.Map;
/**
* erlang虚拟对象
*/
public class ErlangObject {
Map<String,Object> map ;
public ErlangObject(){
map = new HashMap<>();
}
public void putString(String key,String data){
map.put(key,data);
}
public String getString(String key,String data){
if (map.containsKey(key)){
return map.get(key).toString();
}else {
return data;
}
}
public void put(String key,Object data){
map.put(key,data);
}
public Object get(String key){
if (map.containsKey(key)){
return map.get(key);
}else {
return null;
}
}
@Override
public String toString() {
return map.toString();
}
}
复制代码
接下来是主要的转换类,这里使用单例加载。enClass就是解析类,enList就是解析数组。
两种思路差不多,enClass将{}里面的字符一个个遍历,遇到逗号则把遍历到的字符拼成字符串填入erlangObjet的一个属性(当出现括号{与[时,会将符号压入栈,表示这后面一段字符串是一个类或者数组的属性,在出现该括号对应的结束符之前会忽略逗号,出现对应逗号后,在解析这段数据写入erlangObjetobject)
enClass将[]里面的字符一个个遍历,遇到逗号则把遍历到的字符拼成字符串填入数组(当出现括号{与[时,会将符号压入栈,表示这后面一段字符串是一个类或者数组的属性,在出现该括号对应的结束符之前会忽略逗号,出现对应逗号后,再解析这段数据填入数组)
import java.util.*;
/**
* erlang转换工具
*/
public class ErlangManager {
// pair以右括号为key, 左括号为值
private Map<Character, Character> pair = null;
private static ErlangManager erlangManager;
private ErlangManager()
{
pair = new HashMap<Character, Character>();
// pair.put(')', '(');
pair.put('}', '{');
pair.put(']', '[');
}
public static ErlangManager getInstance(){
if (erlangManager==null){
erlangManager = new ErlangManager();
}
return erlangManager;
}
/**
* 转为erlang类
* {}
* @param s
* @return
* @throws ErlangParseException
*/
public ErlangObject enClass(String s) throws ErlangParseException{
if (s==null) throw new ErlangParseException();
s = s.replace("\n","").replace("\r","").replace(" ","");
//不是 {}格式则抛出异常
if (!s.startsWith("{")||!s.endsWith("}")) throw new ErlangParseException();
//长度为2则是空内容
if (s.length()==2) s = "";
//否则去掉两边括号
else s = s.substring(1,s.length()-1);
//用临时栈存储括号
Stack<Character> sc = new Stack<Character>();
//定义一个erlangObject节点
ErlangObject erlangObject = new ErlangObject();
StringBuilder sb = new StringBuilder();
int keyindex = 1; //当前无返回属性名,从1开始做为编号做属性名
for (int i = 0; i < s.length(); i++)
{
Character ch = s.charAt(i);
//遇到逗号如果栈是空的,就存为一个属性,否则当当前属性的内容
if (ch==','&&sc.empty()){
//递归存储erlang对象
erlangObject.put(keyindex + "",getObject(sb.toString()));
keyindex++;
sb.delete( 0, sb.length() );
continue;
}
sb.append(ch);
if (pair.containsValue(ch))// 如果是左括号,放⼊栈中
{
sc.push(ch);
} else if (pair.containsKey(ch)&&!sc.empty()&&sc.peek() == pair.get(ch)) // 如果匹配到对应的右括号就出栈
{
sc.pop();
}
//读取完字符串把最后一个数据放入虚拟erlang对象
if (i == s.length()-1){
erlangObject.put(keyindex + "",getObject(sb.toString()));
}
}
return erlangObject;
}
/**
* 转为object数组
* []
* @param s
* @return
* @throws ErlangParseException
*/
public List<Object> enList(String s) throws ErlangParseException{
if (s==null) throw new ErlangParseException();
s = s.replace("\n","").replace("\r","").replace(" ","");
if (!s.startsWith("[")||!s.endsWith("]")) throw new ErlangParseException();
if (s.length()==2) s = "";
else s = s.substring(1,s.length()-1);
Stack<Character> sc = new Stack<Character>();
List<Object> erlangObjectList = new ArrayList<>();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < s.length(); i++)
{
Character ch = s.charAt(i);
if (ch==','&&sc.empty()){
erlangObjectList.add(getObject(sb.toString()));
sb.delete( 0, sb.length() );
continue;
}
sb.append(ch);
if (pair.containsValue(ch))// 如果是左括号,放⼊栈中
{
sc.push(ch);
} else if (pair.containsKey(ch)&&!sc.empty()&&sc.peek() == pair.get(ch)) // 如果是右括号
{
sc.pop();
}
if (i == s.length()-1){
erlangObjectList.add(getObject(sb.toString()));
}
}
return erlangObjectList;
}
/**
* 通过字符串前缀获取object
* @param str
* @return
*/
private Object getObject(String str){
try {
if (str.startsWith("{")) {
return enClass(str);
} else if (str.startsWith("[")) {
return enList(str);
} else {
return str;
}
}catch (Exception e){
return str;
}
}
}
复制代码
还要为我们的转换定制一个异常
/**
* Created by dengzhenli on 2019/1/5.
* erlang定制异常
*/
public class ErlangParseException extends RuntimeException {
private static final long serialVersionUID = 1L;
public ErlangParseException(){
super();
}
public ErlangParseException(String message){
super(message);
}
public ErlangParseException(String message, Throwable cause){
super(message, cause);
}
}
复制代码
测试一下
public class ErTest {
public static void main(String[] args) {
//
ErlangManager judger = ErlangManager.getInstance();
try {
System.out.println(judger.enClass(null));
System.out.println(judger.enClass("{{1,7,[1,[2,3]]},3}"));
System.out.println(judger.enClass("{player_yaodan,11166672,885,[{yaodan,809,35,1,1600,[]},{yaodan,795,47,1,1600,[]},{yaodan,827,90,1,1600,[]},{yaodan,829,29,1,1600,[]},{yaodan,819,66,1,1600,[]},{yaodan,733,41,1,1600,[]},{yaodan,738,11,1,1600,[]},{yaodan,745,78,1,1600,[]},{yaodan,747,5,1,1600,[]},{yaodan,751,47,1,1600,[]},{yaodan,679,90,1,1600,[]},{yaodan,692,96,1,1600,[]},{yaodan,494,96,1,1600,[]}],[],[],400,[2,5,1],true}"));
}catch (Exception e){
e.printStackTrace();
}
}
}
复制代码
运行结果如下,可看到成功解析出了我们想要的格式