• 多叉树结构:JSON数据解析(一)


    多叉树结构:JSON数据解析(一)


            最近做一个实时数据搜索引擎的项目中,在项目架构的偏顶层需要写一个JSON数据解析的模块,由于目前JSON解析没有现成统一开源框架可以利用,目前只是手工利用com.alibaba.fastjson的API来自行转换,非常麻烦且不简洁,由此想到写一个通用工具类,封装成jar包以供调用。

            先说下整体实时数据搜索引擎这个产品的整体架构图,以及JSON解析模块的位置:

    一、准备工作

            JSON是一种通用格式数据,通常手工解析提取字段是利用com.alibaba.fastjson的API来解析。首先需要用mvn配置好pom.xml,具体fastjson的开源工具jar包在这里可以下载:http://www.mvnrepository.com/artifact/com.alibaba/fastjson,这里随意选择一个版本,把依赖字段配置进pom.xml中,并在项目构建路径导入下载好的fastjson-1.1.41.jar。

    在pom.xml中配置依赖:

              <dependency>

                       <groupId>com.alibaba</groupId>

                       <artifactId>fastjson</artifactId>

                       <version>1.1.41</version>

             </dependency>

            JSON说到底就是一个字符串String,并且利用fastjsonAPI已经可以直接提取其中的关键字段,无需再从头解析。

    首先,这里有几个函数说明下:最常用到的是getJSONObject()getJSONArray()函数

    1.      JSONObject getJSONObject(Stringkey);提取json字符串里的字段作为JSONObject

    2.      JSONArray getJSONArray(Stringkey);  提取JSONObject数组中的字段作为JSONArray

    3.      Object parse(String text); 把JSON文本parse转换为JSONObject或JSONArray

    4.      JSONObject parseObject(Stringtext);把JSON文本转换为JSONObject

    5.      <T> T parsrObject(Stringtext, Class<T>, clazz); 把JSON文本parse为JavaBean(键值对)

    6.      JSONArray parseArray(Stringtext); 把JSON文本转换为JSONArray

    7.      <T> List<T>parseArray(String text, class <T> clazz); 把JSON文本转为JavaBean集合

    8.      String toJSONString(Objectobject); 把JavaBean序列化为JSON文本

    9.      String toJSONString(Objectobject, Boolean prettyFormat);将JavaBean序列化为带格式JSON文本

    10.  Object toJSON(ObjectjavaObject); 将JavaBean转换为JSONObject或JSONArray


    二、JSON数据转换样例

            这里先给个在线查看JSON数据格式的层级关系工具:http://tool.oschina.net/codeformat/json,有了这个东西,查看JSON数据结构就方便多啦……

            先来个开胃菜,上个简单的,假设只有两层数据嵌套,先上源码:

    import com.alibaba.fastjson.JSONArray;
    import com.alibaba.fastjson.JSONObject;
    
    public class FastJson {
    	public static void main(String[] args){
    	    String strJson = "{"sqls":[{"sql":"INSERT INTO T_BASE_PERSON (PERSON_ID,PERSON_NAME) VALUES (?,?)","values":"0dc11abb-967d-11e3-afc0-000c29c3253b,张三"},{"sql":"UPDATE T_BASE_CLASS SET CLASS_NAME=? WHERE CLASS_ID=?","values":"一年一班,538b7ee7-967d-11e3-afc0-000c29c3253b"}]}";
                    
            JSONObject myObj = JSONObject.parseObject(strJson);
            JSONArray myArray = myObj.getJSONArray("sqls"); //就是根节点"sql"冒号后的东西[{"sql":"INSERT INTO...", "values":"0dc11abb"}]
            System.out.println(myArray);
            for(int i=0; i<myArray.size(); i++)
            {
            	JSONObject o = myArray.getJSONObject(i);
            	System.out.println(o);  //o就是第一个花括号{}里包含的所有东西(包括"sql","value"及其所有值)
                System.out.println(o.get("sql"));     //o.get("sql")就是得到sql冒号后的东西"INSERT INTO..."
                System.out.println(o.get("values"));  //o.get("value")就是得到value冒号后的东西"0dc11abb..."
            }
            
            String strJson1 = "[{"sql":"INSERT INTO T_BASE_PERSON (PERSON_ID,PERSON_NAME) VALUES (?,?)","values":"0dc11abb-967d-11e3-afc0-000c29c3253b,张三"},{"sql":"UPDATE T_BASE_CLASS SET CLASS_NAME=? WHERE CLASS_ID=?","values":"一年一班,538b7ee7-967d-11e3-afc0-000c29c3253b"}]";
             
            JSONArray myArray1 = JSONArray.parseArray(strJson1);
            System.out.println(myArray1);
            for(int j=0; j<myArray1.size(); j++)
            {
                JSONObject o1 = myArray1.getJSONObject(j);
                System.out.println(o1.get("sql"));
                System.out.println(o1.get("values"));
            }
    	}		
    	
    }
    

            可以看到,解析JSON字符串,最关键无非就是两个步骤:getJSONObject、getJSONArray,然后for循环遍历Array取得相应字段json对象。

    看下运行结果吧:



    三、JSON数据转换——穷举遍历法、无根节点多叉树随机访问

            项目中需要解析的json样本数据,层级结构非常多,层层嵌套,有的甚至可能达10+层甚至30+层之多,如果仍旧如同上例每一层一层地去嵌套地利用for循环解析,采用硬编码指定固定字段名称的方式,效率非常低下,并且程序可扩展性太差。

            我们的目的就是要把json里的字段全部有序地提取出来,只提取”aggregation”字段下的数据,并且能支持随机访问每个字段,可以用硬编码方式,也可以用优化的方式,即采用一个好的数据结构来对应这种json数据格式。

            这里先给个样例,看下数据嵌套的规模吧。(注意这里为了区分单值和多值节点,首先对样本数据作了个小处理:凡是该节点下没有多个数组的为单值节点,全部以”ss_”开头作为标记,凡是该节点下有多个数组的为多值节点,全部以”bb_”开头作为标记,并且”bb_”开头的下面可能还有多值节点,即又是”bb_”开头的数据):


    3.1 最笨的方法——穷举循环遍历

            最开始没有什么好的方法,也还没想好什么数据结构来应对,先一步一步硬编码的方式去解析,大不了就是体力活,层层地“剥洋葱”,这里先给出源码:

    import java.io.File;
    import java.io.IOException;
    //import java.util.Iterator;
    //import java.util.LinkedHashMap;
    import org.apache.commons.io.FileUtils;
    import com.alibaba.fastjson.JSONArray;
    import com.alibaba.fastjson.JSONObject;
    
    public class DataFour {
    	public static void main(String[] args) throws IOException {		
    		DataFour d = new DataFour();
    		String json = FileUtils.readFileToString(new File("e:/data/json4.txt"));		
    		JSONObject jsono = JSONObject.parseObject(json);		
    		JSONObject b = jsono.getJSONObject("aggregations"); //"预处理",得到"aggregation"下的所有字段
    		//d.buildResult(b);  //------原始样例函数buildResult(b)-------//
    		d.Function(b,0);     //------(新建)逐层解析函数Function(b)-----//
    	}
    	
    	public void Function(JSONObject b, int level){
    		String levelstr = level + "";
    		for(int i=0; i<level; i++){
    			levelstr += "	";
    		}
    		for(String key : b.keySet()){
    			if(key.startsWith("bb_")){				
    				JSONObject buckets = b.getJSONObject(key);
    				JSONArray bArray = buckets.getJSONArray("buckets");
    				for(int i=0; i<bArray.size(); i++){
    					JSONObject o = bArray.getJSONObject(i);
    					System.out.println("level"+level+"key:"+o.get("key")); //key:UZ6958037483314
    					System.out.println("level"+level+"doc_count:"+o.get("doc_count")); //doc_count:361
    					
    					JSONObject vcod_buckets = o.getJSONObject("bb_versionCode");
    					JSONArray vcod_Array = vcod_buckets.getJSONArray("buckets");
    					System.out.println("level"+level+"bb_versionCode:");
    					for(int j=0; j<vcod_Array.size(); j++){
    						JSONObject o1 = vcod_Array.getJSONObject(j);
    						System.out.println("level"+(level+1)+"	"+"key:" + o1.get("key"));
    						System.out.println("level"+(level+1)+"	"+"doc_count:" + o1.get("doc_count"));
    						
    						JSONObject isNewVersion_buckets = o1.getJSONObject("bb_isNewVersion");
    						JSONArray isNewVersion_Array = isNewVersion_buckets.getJSONArray("buckets");
    						System.out.println("level"+(level+1)+"	"+"bb_isNewVersion:");
    						for(int k=0; k<isNewVersion_Array.size(); k++){
    							JSONObject o2 = isNewVersion_Array.getJSONObject(k);
    							System.out.println("level"+(level+2)+"	"+"	"+"key:"+o2.get("key"));
    							System.out.println("level"+(level+2)+"	"+"	"+"doc_count:"+o2.get("doc_count"));
    						}
    						
    						JSONObject isNew_buckets = o1.getJSONObject("bb_isNew");
    						JSONArray isNew_Array = isNew_buckets.getJSONArray("buckets");
    						System.out.println("level"+(level+1)+"	"+"bb_isNew:");
    						for(int k=0; k<isNew_Array.size(); k++){
    							JSONObject o2 = isNew_Array.getJSONObject(k);
    							System.out.println("level"+(level+2)+"	"+"	"+"key:"+o2.get("key"));
    							System.out.println("level"+(level+2)+"	"+"	"+"doc_count:"+o2.get("doc_count"));
    						}
    						
    						System.out.println("level"+(level+1)+"	"+"ss_usercount/value:" + o1.getJSONObject("ss_usercount").getString("value"));
    					}
    				}
    			}
    		}
    	}
    }


    运行结果:


            虽然通过这样的方式的确能把数据提取出来,但是代码中充斥着大量重复代码段,完全可以进行代码级重构,即把公用代码抽取出来,作为公有函数调用。但这种方式,代码量不会减少太多,并且效率得不到提升。


            先写到这里,优化的方法到下篇文章再写。

    (原创文章,转载请注明出处)


  • 相关阅读:
    AXI协议(一)
    System Verilog基础(一)
    AHB协议
    验证的概述
    简易APB4 slave实践
    APB协议
    指令跳转与预测
    HDU4405--Aeroplane chess(概率dp)
    BZOJ1419——Red is good(期望dp)
    BZOJ1426----收集邮票(期望dp)
  • 原文地址:https://www.cnblogs.com/DianaCody/p/5425684.html
Copyright © 2020-2023  润新知