本版的改进主要在字符串的处理,前版不允许出现[]{},:等,现在都可以了,做出的修改主要在Lexer类,另外Token类增加1了下标,TreeBuilder类的不合语法处也做出一定修改.
测试用例:https://www.cnblogs.com/heyang78/p/12956735.html
代码下载地址:https://files.cnblogs.com/files/heyang78/JsonAnalyzer2-20200525-2.zip
主要代码:
Token类,Json文本最终会被变成Token的链表:
package com.heyang; /** * Tokens in json format * @author Heyang * */ public class Token { public static final int TYPE_OPEN_BRACE=0; // { public static final int TYPE_CLOSE_BRACE=1; // } public static final int TYPE_TEXT=2; // text public static final int TYPE_COMMA=3; // , public static final int TYPE_COLON=4; // : public static final int TYPE_OPEN_BRACKET=5; // [ public static final int TYPE_CLOSE_BRACKET=6; // ] private int type; private String text; private int index;// Used to remember location 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; } public String toString() { return String.format("token(text=%s,type=%d,index=%d)", text,type,index); } public int getType() { return type; } public void setType(int type) { this.type = type; } public String getText() { return text; } public void setText(String text) { this.text = text; } public int getIndex() { return index; } public void setIndex(int index) { this.index = index; } }
Lexer类,此类用于将json文本分成token:
package com.heyang; import java.util.ArrayList; import java.util.List; import org.apache.commons.lang.StringUtils; /** * Parse json string to tokens * @author Heyang * */ public class Lexer { private List<Token> tokens; public Lexer(String jsonTxt) { tokens = new ArrayList<Token>(); String bundle = ""; for (int i = 0; i < jsonTxt.length(); i++) { char c = jsonTxt.charAt(i); if(c==':') { char t=c; char p=t; c=p; } if (Character.isWhitespace(c)) { continue; } else if (c == '{') { tokens.add(new Token(c, Token.TYPE_OPEN_BRACE)); } else if (c == '}') { if (StringUtils.isNotEmpty(bundle)) { tokens.add(new Token(bundle, Token.TYPE_TEXT)); bundle = ""; } tokens.add(new Token(c, Token.TYPE_CLOSE_BRACE)); } else if (c == '[') { tokens.add(new Token(c, Token.TYPE_OPEN_BRACKET)); } else if (c == ']') { if (StringUtils.isNotEmpty(bundle)) { tokens.add(new Token(bundle, Token.TYPE_TEXT)); bundle = ""; } tokens.add( new Token(c, Token.TYPE_CLOSE_BRACKET)); } else if (c == ',') { if (StringUtils.isNotEmpty(bundle)) { tokens.add(new Token(bundle, Token.TYPE_TEXT)); bundle = ""; } tokens.add(new Token(c, Token.TYPE_COMMA)); } else if (c == ':') { if (StringUtils.isNotEmpty(bundle)) { tokens.add(new Token(bundle, Token.TYPE_TEXT)); bundle = ""; } tokens.add(new Token(c, Token.TYPE_COLON)); }else if(c == '"') { int idx=i+1; while(idx<jsonTxt.length()) { char cEnd = jsonTxt.charAt(idx); if (cEnd == '"') { break; } idx++; } String sub=jsonTxt.substring(i, idx+1); tokens.add(new Token(sub, Token.TYPE_TEXT)); i=idx; } else { bundle += c; } } setTokenIndexes(); } public void setTokenIndexes() { int idx = 0; for (Token t : tokens) { idx++; t.setIndex(idx); } } public void printTokens() { int idx = 0; for (Token t : tokens) { idx++; t.setIndex(idx); System.out.println("#" + idx + " " + t.getText()); } } public String getCompactJsonTxt() { StringBuilder sb=new StringBuilder(); for (Token t : tokens) { sb.append(t.getText()); } return sb.toString(); } public List<Token> getTokenList() { return tokens; } }
Node类,代表json节点:
package com.heyang; import java.util.Collections; import java.util.LinkedList; import java.util.List; /** * Json Node * @author Heyang * */ public class Node implements Comparable<Node>{ // 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 Node parent; // There are three types of value private int valueType; private String valueString; private List<Node> valueList; // indent depth private int depth; public Node() { } public Node(String key,String value) { this.key=key; this.valueType=Type_String; this.valueString=value; this.depth=0; } public Node(String key,int type) { this.key=key; this.valueType=type; this.valueList=new LinkedList<Node>(); } public void addChild(Node child) { if(valueList!=null) { valueList.add(child); child.parent=this; adjustDepth(); } } private void adjustDepth() { if(valueType==Type_List || valueType==Type_Array) { for(Node json:valueList) { json.depth=this.depth+1; json.adjustDepth(); } } } public String toString() { StringBuilder sb=new StringBuilder(); // key String tabs=getIndentSpace(); sb.append(tabs); if(key!=null) { sb.append(key); sb.append(":"); } // value if(valueType==Type_String) { sb.append(valueString); }else if(valueType==Type_Array) { sb.append("[ "); int n=valueList.size(); for(int i=0;i<n;i++) { Node json=valueList.get(i); if(i!=n-1) { sb.append(json.toString()+", "); }else { sb.append(json.toString()+" "); } } sb.append(tabs+"]"); }else if(valueType==Type_List) { sb.append("{ "); Collections.sort(valueList); int n=valueList.size(); for(int i=0;i<n;i++) { Node json=valueList.get(i); if(i!=n-1) { sb.append(json.toString()+", "); }else { sb.append(json.toString()+" "); } } sb.append(tabs+"}"); } return sb.toString(); } public int compareTo(Node 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 Node getParent() { return parent; } public void setParent(Node parent) { this.parent = parent; } public List<Node> getValueList() { return valueList; } }
TreeBuilder类,用于构建一棵Node节点树:
package com.heyang; import java.util.List; public class TreeBuilder { private Node root; private List<Token> tokens; private int tokenIdx; public TreeBuilder(List<Token> tokens) throws Exception{ this.tokens=tokens; this.tokenIdx=0; root=new Node(null,Node.Type_List); parse_object(root); } private void parse_object(Node parent) throws Exception{ Token token; token=fetchToken(); if(token.getType()!=Token.TYPE_OPEN_BRACE) { throw new Exception("Expected:'{' actual:"+token.getText()+" "+token); } for(;;) { token=fetchToken(); if(token.getType()!=Token.TYPE_TEXT) { returnToken(); break; } String key=token.getText(); token=fetchToken(); if(token.getType()!=Token.TYPE_COLON) { throw new Exception("Expected:':' actual:"+token.getText()+" "+token); } token=fetchToken(); if(token.getType()==Token.TYPE_TEXT) { String value=token.getText(); parent.addChild(new Node(key,value)); }else if(token.getType()==Token.TYPE_OPEN_BRACE){ Node node=new Node(key,Node.Type_List); parent.addChild(node); returnToken(); parse_object(node); }else if(token.getType()==Token.TYPE_OPEN_BRACKET) { Node node=new Node(key,Node.Type_Array); parse_array(node); parent.addChild(node); }else { throw new Exception("value should be string/object/array but not."); } token=fetchToken(); if(token.getType()==Token.TYPE_COMMA) { continue; }else { returnToken(); break; } } token=fetchToken(); if(token.getType()!=Token.TYPE_CLOSE_BRACE) { throw new Exception("Expected:'}' actual:"+token.getText()+" "+token); } } private void parse_array(Node parent) throws Exception { Token token; for(;;) { token=fetchToken(); if(token.getType()==Token.TYPE_TEXT) { String value=token.getText(); Node node=new Node(null,value); parent.addChild(node); }else if(token.getType()==Token.TYPE_OPEN_BRACE) { Node node=new Node(null,Node.Type_List); parent.addChild(node); returnToken(); parse_object(node); }else { returnToken(); } token=fetchToken(); if(token.getType()==Token.TYPE_COMMA) { continue; }else { returnToken(); break; } } token=fetchToken(); if(token.getType()!=Token.TYPE_CLOSE_BRACKET) { throw new Exception("Expected:']' actual:"+token.getText()+" "+token); } } private Token fetchToken() { if(tokenIdx>=tokens.size()) { return null; }else { Token t=tokens.get(tokenIdx); tokenIdx++; return t; } } private void returnToken() { if(tokenIdx>0) { tokenIdx--; } } public Node getRoot() { return root; } }
运行起来:
package com.heyang; import com.heyang.util.BracketChecker; import com.heyang.util.CommonUtil; import com.heyang.util.Renderer; public class EntryPoint { public static void main(String[] args) { try { // Read context from file String jsonTxt=CommonUtil.readTextFromFile("C:\hy\files\json\01.json"); System.out.println("原文="+jsonTxt); // Is brackets balanced BracketChecker checker=new BracketChecker(); boolean isBalanced=checker.isBalanced(jsonTxt); if(isBalanced==false) { System.out.println(Renderer.paintBrown(checker.getErrMsg())); return; } // Parse json to tokens Lexer lex=new Lexer(jsonTxt); //System.out.println("紧缩文本="+lex.getCompactJsonTxt()); //lex.printTokens(); // Build tree TreeBuilder builder=new TreeBuilder(lex.getTokenList()); Node root=builder.getRoot(); System.out.println("内部排序后文本: "+root); }catch(Exception ex) { System.out.println(Renderer.paintBrown(ex.getMessage())); ex.printStackTrace(); } } }
运行效果:
原文={ "type": "object", "properties": { "first_name": { "type": "string" }, "last_name": { "type": "string" }, "age": { "type": "integer" }, "club": { "type": "object", "properties": { "name": { "type": "string" }, "founded": { "type": "integer" } }, "required": ["name"] } }, "required": ["first_name", "last_name", "age", "club"]} 内部排序后文本: { "properties":{ "age":{ "type":"integer" }, "club":{ "properties":{ "founded":{ "type":"integer" }, "name":{ "type":"string" } }, "required":[ "name" ], "type":"object" }, "first_name":{ "type":"string" }, "last_name":{ "type":"string" } }, "required":[ "first_name", "last_name", "age", "club" ], "type":"object" }
--2020年5月25日--