JsonAnalyzer2 1.01版

本版的改进主要在字符串的处理,前版不允许出现[]{},:等,现在都可以了,做出的修改主要在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日--

原文地址:https://www.cnblogs.com/heyang78/p/12956793.html