【Java/Json】Java对Json进行建模,分词,递归向下解析构建Json对象树
伸手党的福音 代码下载:https://files.cnblogs.com/files/xiandedanteng/JsonLexerBuilder20191202.rar
互联网上成型的对Json进行解析的工具包不少,可用来用去就觉得没意思了,哪怕它是大厂出品,可作为一个API的使用者,用得再爽也觉得缺点什么。
于是就有以下的小小作品,稍微填补一下职业生涯的遗憾,其中代码刚写就,还没时间优化,权且放在这里,算是一条小板凳吧。
先看看解析效果:
效果一:
Raw json={ "status": "0000", "message": "success", "data": { "title": { "id": "001", "name" : "白菜" }, "content": [ { "id": "001", "value":"你好 白菜" }, { "id": "002", "value":"你好 萝卜" } ] }} Formatted json={ "data":{ "content":[ { "id":"001", "value":"你好白菜" }, { "id":"002", "value":"你好萝卜" } ], "title":{ "id":"001", "name":"白菜" } }, "message":"success", "status":"0000" }
效果二:
Raw json={"employees": [{ "firstName":"Bill" , "lastName":"Gates" },{ "firstName":"George" , "lastName":"Bush" },{ "firstName":"Thomas" , "lastName":"Carter" }]} Formatted json={ "employees":[ { "firstName":"Bill", "lastName":"Gates" }, { "firstName":"George", "lastName":"Bush" }, { "firstName":"Thomas", "lastName":"Carter" } ] }
效果三:
Raw json={ "name": "username", "age": 20, "admin": true } Formatted json={ "admin":true, "age":20, "name":"username" }
效果四:
Raw json={ "HeWeather6": [{ "basic": { "cid": "CN101010100", "location": "北京", "parent_city": "北京", "admin_area": "北京", "cnty": "中国", "lat": "39.90498734", "lon": "116.40528870", "tz": "8.0" }, "daily_forecast": [{ "cond_code_d": "103", "cond_code_n": "101", "cond_txt_d": "晴间多云", "cond_txt_n": "多云", "date": "2017-10-26", "hum": "57", "pcpn": "0.0", "pop": "0", "pres": "1020", "tmp_max": "16", "tmp_min": "8", "uv_index": "3", "vis": "16", "wind_deg": "0", "wind_dir": "无持续风向", "wind_sc": "微风", "wind_spd": "5" }, { "cond_code_d": "101", "cond_code_n": "501", "cond_txt_d": "多云", "cond_txt_n": "雾", "date": "2017-10-27", "hum": "56", "pcpn": "0.0", "pop": "0", "pres": "1018", "tmp_max": "18", "tmp_min": "9", "uv_index": "3", "vis": "20", "wind_deg": "187", "wind_dir": "南风", "wind_sc": "微风", "wind_spd": "6" }, { "cond_code_d": "101", "cond_code_n": "101", "cond_txt_d": "多云", "cond_txt_n": "多云", "date": "2017-10-28", "hum": "26", "pcpn": "0.0", "pop": "0", "pres": "1029", "tmp_max": "17", "tmp_min": "5", "uv_index": "2", "vis": "20", "wind_deg": "2", "wind_dir": "北风", "wind_sc": "3-4", "wind_spd": "19" }], "status": "ok", "update": { "loc": "2017-10-26 23:09", "utc": "2017-10-26 15:09" } }]} Formatted json={ "HeWeather6":[ { "basic":{ "admin_area":"北京", "cid":"CN101010100", "cnty":"中国", "lat":"39.90498734", "location":"北京", "lon":"116.40528870", "parent_city":"北京", "tz":"8.0" }, "daily_forecast":[ { "cond_code_d":"103", "cond_code_n":"101", "cond_txt_d":"晴间多云", "cond_txt_n":"多云", "date":"2017-10-26", "hum":"57", "pcpn":"0.0", "pop":"0", "pres":"1020", "tmp_max":"16", "tmp_min":"8", "uv_index":"3", "vis":"16", "wind_deg":"0", "wind_dir":"无持续风向", "wind_sc":"微风", "wind_spd":"5" }, { "cond_code_d":"101", "cond_code_n":"501", "cond_txt_d":"多云", "cond_txt_n":"雾", "date":"2017-10-27", "hum":"56", "pcpn":"0.0", "pop":"0", "pres":"1018", "tmp_max":"18", "tmp_min":"9", "uv_index":"3", "vis":"20", "wind_deg":"187", "wind_dir":"南风", "wind_sc":"微风", "wind_spd":"6" }, { "cond_code_d":"101", "cond_code_n":"101", "cond_txt_d":"多云", "cond_txt_n":"多云", "date":"2017-10-28", "hum":"26", "pcpn":"0.0", "pop":"0", "pres":"1029", "tmp_max":"17", "tmp_min":"5", "uv_index":"2", "vis":"20", "wind_deg":"2", "wind_dir":"北风", "wind_sc":"3-4", "wind_spd":"19" } ], "status":"ok", "update":{ "loc":"2017-10-2623:09", "utc":"2017-10-2615:09" } } ] }
效果五:
Raw json={ "data": [ { "deliveryListId": "20180001", "shipperCode": "0030", "shortShipperName": "RB", "orderNo": "102018032001", "deliveryOrder": 1, "receiverName": "吉田 XXX", "receiverTelNo": "07012340303", "receiverAddress1": "東京都足立区足立1-1", "receiverAddress2": "東京都足立区足立1-2", "isCod": true, "billAmount": 5, "geocodingScore": 50, "latitudeJP": "56789.33", "longitudeJP": "123456.33", "latitude": "20180001.22", "longitude": "20180001.33", "vehicleId": "239", "orderDetails": [ { "trackingNo": "201803200001", "quantity": 1, "lapCount": null, "statusCode": null, "statusNameMobile": null }, { "trackingNo": "201803200002", "quantity": 1, "lapCount": 4, "statusCode": "100", "statusNameMobile": "配送準備中" }, { "trackingNo": "201803200003", "quantity": 1, "lapCount": 4, "statusCode": "300", "statusNameMobile": "持出し" }, { "trackingNo": "201803200004", "quantity": 1, "lapCount": 4, "statusCode": "100", "statusNameMobile": "配送準備中" }, { "trackingNo": "201803200005", "quantity": 1, "lapCount": 4, "statusCode": "100", "statusNameMobile": "配送準備中" } ] } ]} Formatted json={ "data":[ { "billAmount":5, "deliveryListId":"20180001", "deliveryOrder":1, "geocodingScore":50, "isCod":true, "latitude":"20180001.22", "latitudeJP":"56789.33", "longitude":"20180001.33", "longitudeJP":"123456.33", "orderDetails":[ { "lapCount":null, "quantity":1, "statusCode":null, "statusNameMobile":null, "trackingNo":"201803200001" }, { "lapCount":4, "quantity":1, "statusCode":"100", "statusNameMobile":"配送準備中", "trackingNo":"201803200002" }, { "lapCount":4, "quantity":1, "statusCode":"300", "statusNameMobile":"持出し", "trackingNo":"201803200003" }, { "lapCount":4, "quantity":1, "statusCode":"100", "statusNameMobile":"配送準備中", "trackingNo":"201803200004" }, { "lapCount":4, "quantity":1, "statusCode":"100", "statusNameMobile":"配送準備中", "trackingNo":"201803200005" } ], "orderNo":"102018032001", "receiverAddress1":"東京都足立区足立1-1", "receiverAddress2":"東京都足立区足立1-2", "receiverName":"吉田XXX", "receiverTelNo":"07012340303", "shipperCode":"0030", "shortShipperName":"RB", "vehicleId":"239" } ] }
下面是三个类的代码:
Json对象类:
package com.hy; import java.util.Collections; import java.util.LinkedList; import java.util.List; /** * Json对象类 * @author 逆火 * * 2019年12月2日 下午8:17:06 */ public class Json implements Comparable<Json>{ // There are value types public static final int Type_String=1; public static final int Type_Array=2; public static final int Type_List=3; // Key always is String private String key; private Json parent; // There are three types of value private int valueType; private String valueString; private List<Json> valueList; // indent depth private int depth; public Json() { } /** * Contructor1 */ public Json(String key,String value) { this.key=key; this.valueType=Type_String; this.valueString=value; this.depth=0; } public Json(String key,int type) { this.key=key; if(type==Type_List) { this.valueType=Type_List; this.valueList=new LinkedList<Json>(); }else if(type==Type_Array) { this.valueType=Type_Array; this.valueList=new LinkedList<Json>(); } } public List<Json> getValueList() { return valueList; } public void addJsonToList(Json json) { if(valueList!=null) { valueList.add(json); json.parent=this; adjustDepth(); } } public void addJsonToArray(Json json) { if(valueList!=null) { valueList.add(json); json.parent=this; adjustDepth(); } } private void adjustDepth() { if(valueType==Type_List) { for(Json json:valueList) { json.depth=this.depth+1; json.adjustDepth(); } } if(valueType==Type_Array) { for(Json json:valueList) { json.depth=this.depth+1; json.adjustDepth(); } } } public String toString() { StringBuilder sb=new StringBuilder(); // key String tabs=getIndentSpace(); sb.append(tabs); //sb.append("\""+(key==null?"":key)+"\""); if(key!=null) { //sb.append("\""+key+"\"");// 以对象构建时恢复 sb.append(key);// 以文件构建时打开 sb.append(":"); }else { } // value if(valueType==Type_String) { //sb.append("\""+valueString+"\"");// 以对象构建时恢复 sb.append(valueString);// 以文件构建时打开 }else if(valueType==Type_Array) { sb.append("[\n"); int n=valueList.size(); for(int i=0;i<n;i++) { Json json=valueList.get(i); if(i!=n-1) { sb.append(json.toString()+",\n"); }else { sb.append(json.toString()+"\n"); } } sb.append(tabs+"]"); }else if(valueType==Type_List) { sb.append("{\n"); Collections.sort(valueList); int n=valueList.size(); for(int i=0;i<n;i++) { Json json=valueList.get(i); if(i!=n-1) { sb.append(json.toString()+",\n"); }else { sb.append(json.toString()+"\n"); } } sb.append(tabs+"}"); } return sb.toString(); } public int compareTo(Json other) { return this.key.compareTo(other.key); } private String getIndentSpace() { return String.join("", Collections.nCopies(this.depth, " ")); } public String getKey() { return key; } public void setKey(String key) { this.key = key; } public Json getParent() { return parent; } public void setParent(Json parent) { this.parent = parent; } public static void main(String[] args) { Json id1=new Json("id","001"); Json name1=new Json("name","鐧借彍"); Json title=new Json("title",3); title.addJsonToList(id1); title.addJsonToList(name1); Json empty1=new Json(null,3); empty1.addJsonToList(new Json("id","001")); empty1.addJsonToList(new Json("id","浣犲ソ鐧借彍")); Json empty2=new Json(null,3); empty2.addJsonToList(new Json("id","001")); empty2.addJsonToList(new Json("id","浣犲ソ钀濆崪")); Json content=new Json("content",2); content.addJsonToArray(empty1); content.addJsonToArray(empty2); Json data=new Json("data",3); data.addJsonToList(title); data.addJsonToList(content); Json status=new Json("status","0000"); Json message=new Json("message","success"); Json root=new Json(null,3); root.addJsonToList(status); root.addJsonToList(message); root.addJsonToList(data); System.out.println(root.toString()); } }
分词器类:
package com.hy; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.List; import org.apache.commons.lang.StringUtils; class Token{ static final int TYPE_LBRACE=0;// 宸﹀ぇ鎷彿 static final int TYPE_RBRACE=1;// 鍙冲ぇ鎷彿 static final int TYPE_TEXT=2;// 鏂囨湰 static final int TYPE_COMMA=3;// 閫楀彿 static final int TYPE_COLON=4;// 鍐掑彿 static final int TYPE_LBRACKET=5;// 宸︿腑鎷彿 static final int TYPE_RBRACKET=6;// 鍙充腑鎷彿 int type; String text; public Token(char c,int type) { this.text=String.valueOf(c); this.type=type; } public Token(String word,int type) { this.text=word; this.type=type; } } /** * Json鏂囨湰鍒嗚瘝鍣� * @author 閫嗙伀 * * 2019骞�12鏈�1鏃� 涓婂崍11:35:43 */ public class Lexer { private List<Token> tokenList; /** * Contructor * @param jsonStr */ public Lexer(String jsonStr) { tokenList=new ArrayList<Token>(); String line=""; for(int i=0;i<jsonStr.length();i++){ char c=jsonStr.charAt(i); if(Character.isWhitespace(c)){ continue; }else if(c=='{'){ Token t=new Token(c,Token.TYPE_LBRACE); tokenList.add(t); }else if(c=='}'){ if(StringUtils.isNotEmpty(line)) { Token w=new Token(line,Token.TYPE_TEXT); tokenList.add(w); line=""; } Token t=new Token(c,Token.TYPE_RBRACE); tokenList.add(t); }else if(c=='['){ Token t=new Token(c,Token.TYPE_LBRACKET); tokenList.add(t); }else if(c==']'){ Token t=new Token(c,Token.TYPE_RBRACKET); tokenList.add(t); }else if(c==',') { if(StringUtils.isNotEmpty(line)) { Token w=new Token(line,Token.TYPE_TEXT); tokenList.add(w); line=""; } Token t=new Token(c,Token.TYPE_COMMA); tokenList.add(t); }else if(c==':') { if(StringUtils.isNotEmpty(line)) { Token w=new Token(line,Token.TYPE_TEXT); tokenList.add(w); line=""; } Token t=new Token(c,Token.TYPE_COLON); tokenList.add(t); }else { line+=c; } } } public List<Token> getTokenList() { return tokenList; } public void printTokens() { int idx=0; for(Token t:tokenList) { idx++; System.out.println("#"+idx+" "+t.text); } } /** * Entry point */ public static void main(String[] args) { String filePathname="D:\\logs\\20191126-1.json"; try { StringBuilder sb=new StringBuilder(); BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(filePathname), "UTF-8")); String line = null; while( ( line = br.readLine() ) != null ) { sb.append(line); } br.close(); String jsonStr=sb.toString(); System.out.println("Raw json="+jsonStr); Lexer l=new Lexer(jsonStr); l.printTokens(); } catch (FileNotFoundException ex) { ex.printStackTrace(); } catch (IOException ex) { ex.printStackTrace(); } } }
Json对象构建类:
package com.hy; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStreamReader; import java.util.List; import java.util.Stack; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Json对象构建器 * @author 逆火 * * 2019年12月2日 下午8:22:59 */ public class Builder { private Json root; private int index; private List<Token> tokens; public Json getRoot() { return root; } public Builder(List<Token> tokens) { this.root=null; this.tokens=tokens; this.index=1; root=createObjJson(); build(root); } /** * 递归向下 * @param parent */ private void build(Json parent) { if(parent==null) { return; } Stack<Token> stack=new Stack<Token>(); while(index<this.tokens.size()) { Token token=tokens.get(index); if(token.type==Token.TYPE_LBRACE) { Json obj=new Json(null,Json.Type_List); if(stack.size()>=2) { Token left1=stack.pop(); Token left2=stack.pop(); if(left1.type==Token.TYPE_COLON && left2.type==Token.TYPE_TEXT) { obj.setKey(left2.text); } } parent.addJsonToList(obj); index++; build(obj); }else if(token.type==Token.TYPE_RBRACE) { StringBuilder sb=new StringBuilder(); for(int i=0;i<stack.size();i++) { Token t=stack.elementAt(i); sb.append(t.text); } if(sb.length()>0) { java.util.regex.Pattern pattern=Pattern.compile("(\"([_a-zA-Z]+[_a-zA-Z0-9]*)\")[:]([^,}]+)"); Matcher matcher=pattern.matcher(sb.toString()); while(matcher.find()) { Json txt=new Json(matcher.group(1),matcher.group(3)); parent.addJsonToArray(txt); } } stack.clear(); index++; build(parent.getParent()); }else if(token.type==Token.TYPE_LBRACKET) { Json obj=new Json(null,Json.Type_Array); if(stack.size()>=2) { Token left1=stack.pop(); Token left2=stack.pop(); if(left1.type==Token.TYPE_COLON && left2.type==Token.TYPE_TEXT) { obj.setKey(left2.text); } } parent.addJsonToList(obj); index++; build(obj); }else if(token.type==Token.TYPE_COMMA) { StringBuilder sb=new StringBuilder(); for(int i=0;i<stack.size();i++) { Token t=stack.elementAt(i); sb.append(t.text); } if(sb.length()>0) { java.util.regex.Pattern pattern=Pattern.compile("(\"([_a-zA-Z]+[_a-zA-Z0-9]*)\")[:]([^,}]+)"); Matcher matcher=pattern.matcher(sb.toString()); while(matcher.find()) { Json txt=new Json(matcher.group(1),matcher.group(3)); parent.addJsonToList(txt); } } stack.clear(); index++; }else if(token.type==Token.TYPE_RBRACKET) { StringBuilder sb=new StringBuilder(); for(int i=0;i<stack.size();i++) { Token t=stack.elementAt(i); sb.append(t.text); } if(sb.length()>0) { java.util.regex.Pattern pattern=Pattern.compile("(\"([_a-zA-Z]+[_a-zA-Z0-9]*)\")[:]([^,}]+)"); Matcher matcher=pattern.matcher(sb.toString()); while(matcher.find()) { Json txt=new Json(matcher.group(1),matcher.group(3)); parent.addJsonToList(txt); } } stack.clear(); index++; build(parent.getParent()); }else { stack.push(token); index++; } } } private Json createObjJson() { return new Json(null,Json.Type_List); } /** * Entry point */ public static void main(String[] args) { String filePathname="D:\\logs\\train.json";// 注意暂时不接受根为数组的文件 try { StringBuilder sb=new StringBuilder(); BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(filePathname), "UTF-8")); String line = null; while( ( line = br.readLine() ) != null ) { sb.append(line); } br.close(); String jsonStr=sb.toString(); System.out.println("Raw json="+jsonStr); Lexer l=new Lexer(jsonStr); //l.printTokens(); Builder b=new Builder(l.getTokenList()); Json root=b.getRoot(); System.out.println("Formatted json="+root); } catch (FileNotFoundException ex) { ex.printStackTrace(); } catch (IOException ex) { ex.printStackTrace(); } } }
嵌套结构解析用递归就对了,栈结构也是利器之一。
--END-- 2019年12月2日20:36:46