【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

posted @ 2019-12-02 20:37  逆火狂飙  阅读(314)  评论(2编辑  收藏  举报
生当作人杰 死亦为鬼雄 至今思项羽 不肯过江东