亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

服務器端json數據文件分割合并解決方案

2019-11-14 21:14:57
字體:
來源:轉載
供稿:網友
服務器端json數據文件分割合并解決方案

問題引入

Json是什么就不多說了,本文把Json理解成一種協議。

印象之中,Json貌似是前端的專屬,其實不然,服務器端組織數據,依然可以用Json協議。

比如說,某公司有一套測評題目(基于Json協議),這些題目比較珍貴,不想直接放在js中,所以就將題目文件放在服務器端,然后通過一個接口去請求,多一層控制,就多了一層保護,通過在接口上加權限,可保證數據安全。

如此一來,服務器端必定會有一個Json文件(純文本文件),Json文件中包含Json數據。

假設Json數據結構如下:

 1 { 2   "name": "題庫", 3   "items": [{ 4     "name": "測評-1", 5     "items": [/*...*/] 6   },{ 7     "name": "測評-2", 8     "items": [/*...*/] 9   },{10     "name": "測評-3",11     "items": [/*...*/]12   }/*...*/]13 }

暫不討論這樣設計的合理性,假定已經是這么設計了,無法再做更改。但凡是有些規模的項目,需求變動都比較頻繁,項目工期也比較緊張,不得不做出妥協,完美的設計是不存在的。

隨著時間和規模的增長,測評會越來越多,而且每個測評本身包含的數據也不少,這樣一來,這個Json文件會越來越大。

眾所周知,IO操作是一個巨大的瓶頸,如果Json文件太大,占用IO過多,將導致性能嚴重下降。同時Json文件太大,也不好管理,不符合開閉原則。

因此,我們迫切需要對Json文件進行拆分,把數據量大、獨立性強、自成一體的Json數據轉移到主體的外部,單獨放在一個Json文件中,這樣不僅縮小了單個文件的體積,也方便管理。

其實這樣做最大的優點是可以實現懶加載,或者說是按需加載。

這樣的情景很常見,比如在進行數據檢索時,一般情況下,會先看到一個數據概要列表,列出幾項重要信息,其他次要信息需要點擊“詳情”按鈕時,才去加載。

拿上邊測評的例子來說,第一步僅需顯示出有哪些測評,然后根據用戶的選擇,再去加載對應測評的詳細信息。沒有必要一上來就把所有的信息都返回給客戶端,不僅浪費資源,還降低了數據安全性。

如何才能實現Json文件的合并呢?請看下章~~~

解決方案:Jean

Jean是一個java工具類,她可以實現Json文件合并、依賴管理,靈感來自于前端模塊化開發。

這名字是怎么來的呢?前端模塊化開發,國內比較厲害的就是Sea.js了,小菜要寫的是Java工具類,要不就叫Jea?于是趕緊上網查查Jea有沒有啥特殊含義,萬一是敏感詞就不好了。結果一查,查到了Jean,可翻譯為“珍”,相當不錯的名字嘛,就是她了!

Jean的思想是在Json文件中,加入一段特殊代碼,來引入其他Json文件,有點像jsp中的include。語法為:@Jean("family","./items/family.js")??梢园袬Jean()理解成函數調用,里邊有兩個參數,第一個參數是屬性名稱,第二個參數是依賴文件的相對路徑。

文章開篇測評的例子,可以寫成這樣:

 1 { 2   "name": "題庫", 3   "items": [{ 4     "name": "測評-1", 5     @Jean("items","./items1/test.js") 6   },{ 7     "name": "測評-2", 8     @Jean("items","./items2/test.js") 9   },{10     @Jean("items","./items3/test.js"),11     "name": "測評-3"12   }/*...*/]13 }

假設./items1/test.js中內容為:

1 {2   name: "測評-1-內容"3 }

由此可以看出,@Jean在Json文件中的寫法,就和普通的屬性寫法一樣,如果是寫在最后邊,末尾就不用加逗號,其他情況同樣需要加逗號。

通過工具類解析之后,@Jean("items","./items1/test.js")會變成:"items": {name: "測評-1-內容"},替換之后,為了保證格式正確,所以寫@Jean的時候需要按照正常的語法加逗號。

第一個參數,將會轉換成@Jean占位符被替換后的Json屬性名稱,如果不寫,默認為"jean"。

第二個參數是該屬性依賴的Json文件的相對路徑,當然是相對于當前Json文件的,Jean會根據當前Json文件的路徑,找到依賴的Json文件,然后讀取內容,再合并到當前Json文件中。目前小菜實現的Jean工具類,只能識別./和../兩種相對路徑語法(含義與HTML相對路徑語法相同)。

所以,@Jean僅僅是一個占位符,包含有@Jean的Json字符串,必須經過Jean工具類處理之后,才是合法的Json字符串。同時,Jean僅僅關心依賴,而不關心依賴的組織形式,這樣可以帶來巨大的靈活性,無論怎樣組織文件結構,最終體現到Jean的僅僅是一個相對路徑而已。

Jean工具類提供了三個public方法:

 1 /** 2  * 解析所有的jean表達式 3  * @param json json字符串 4  * @param jsonPath json字符串所在路徑,完整路徑 5  * @return 解析后的json字符串 6  */ 7 public static String parseAll(String json,String jsonPath){} 8  9 /**10  * 解析單個jean表達式11  * @param exPRess jean表達式12  * @param jsonPath json字符串所在路徑,完整路徑13  * @return 解析結果14  */15 public static String parSEOne(String express,String jsonPath){}16 17 /**18  * 解析特定的jean表達式19  * @param json json字符串20  * @param jsonPath json字符串所在路徑,完整路徑21  * @param names 需要解析的屬性名稱列表22  * @return 解析后的json字符串23  */24 public static String parseTarget(String json,String jsonPath,List<String> names){}

第一個方法就是說給我一個包含@Jean的Json字符串,再給我這個Json字符串所在文件的絕對路徑,我就把所有的@Jean解析成依賴文件中的內容。

為啥非要單獨傳入一個絕對路徑呢?其實可以直接傳入Json文件的路徑,這樣既能拿到需要解析的Json字符串,又能獲取當前Json文件的絕對路徑。但這樣有一個缺點,就是每調用一次,就要讀一次文件,小菜單獨把路徑寫成一個參數,就是要把讀文件的過程留給用戶,具體怎么讀,由用戶說了算,最終把需要解析的Json字符串和參照路徑給我就可以了。例如:

1 String json = "{@Jean(/"item1/",/"./../../item.js/"),@Jean(/"item2/",/"../item.js/")}";2 System.out.println(parseAll(json, "E:/root/json")); //print {"item1": {"name": "xxx1"},"item2": {"name": "xxx2"}}

第二個方法可以直接解析一個@Jean表達式,不多解釋。例如:

1 String expression = "@Jean(/"item1/",/"./../../item.js/")";2 System.out.println(parseOne(expression, "E:/root/json")); //print "item1": {"name": "xxx1"}

第三個方法可以解析指定的@Jean表達式,@Jean表達式第一個參數是屬性名稱,想解析哪個屬性,就把它放在List<String>中,其他不做解析的,屬性值為null。這樣就實現了懶加載。例如:

1 List<String>  names = new ArrayList<String>();2 names.add("item1");3 String json = "{@Jean(/"item1/",/"./../../item.js/"),@Jean(/"item2/",/"../item.js/")}";4 System.out.println(parseTarget(json, "E:/root/json", names)); //print {"item1": {"name": "xxx"},"item2": null}

Jean源碼

  1 import java.io.BufferedReader;  2 import java.io.File;  3 import java.io.FileInputStream;  4 import java.io.IOException;  5 import java.io.InputStreamReader;  6 import java.util.ArrayList;  7 import java.util.HashMap;  8 import java.util.List;  9 import java.util.Map; 10 import java.util.regex.Matcher; 11 import java.util.regex.Pattern; 12  13  14 /** 15  * json文件合并工具類 16  * @author 楊元 17  */ 18 public class Jean { 19      20     /** 21      * 識別jean表達式 22      */ 23     private static Pattern jeanRegex = Pattern.compile("(@Jean//((/"[^/"]*/",)?/"[^/"]*/"http://))"); 24     /** 25      * 識別jean表達式中的所有參數 26      */ 27     private static Pattern paramRegex = Pattern.compile("/"([^/"]*)/""); 28     /** 29      * 識別jean表達式中的name參數 30      */ 31     private static Pattern nameRegex = Pattern.compile("/"([^/"]*)/","); 32     /** 33      * 默認屬性名稱 34      */ 35     private static String defaultName = "jean"; 36      37     /** 38      * 解析所有的jean表達式 39      * @param json json字符串 40      * @param jsonPath json字符串所在路徑,完整路徑 41      * @return 解析后的json字符串 42      */ 43     public static String parseAll(String json,String jsonPath){ 44         //識別jean表達式 45         List<String> jeans = regexMatchList(jeanRegex, json); 46         jeans = noRepeat(jeans); 47          48         //解析 49         for(String jean : jeans){ 50             json = json.replace(jean, parse(jean, jsonPath)); 51         } 52          53         return json; 54     } 55      56     /** 57      * 解析單個jean表達式 58      * @param express jean表達式 59      * @param jsonPath json字符串所在路徑,完整路徑 60      * @return 解析結果 61      */ 62     public static String parseOne(String express,String jsonPath){ 63         return parse(express, jsonPath); 64     } 65      66     /** 67      * 解析特定的jean表達式 68      * @param json json字符串 69      * @param jsonPath json字符串所在路徑,完整路徑 70      * @param names 需要解析的屬性名稱列表 71      * @return 解析后的json字符串 72      */ 73     public static String parseTarget(String json,String jsonPath,List<String> names){ 74         //識別jean表達式 75         List<String> jeans = regexMatchList(jeanRegex, json); 76         jeans = noRepeat(jeans); 77         //處理屬性名映射 78         Map<String, Boolean> nameMap = new HashMap<String, Boolean>(); 79         for(String s : names){ 80             nameMap.put(s, true); 81         } 82          83         //解析 84         String replacement = ""; 85         Matcher matcher = null; 86         String name = ""; 87         for(String jean : jeans){ 88             matcher = nameRegex.matcher(jean); 89              90             //判斷是否傳入屬性名稱 91             if(matcher.find()){ 92                 name = matcher.group(1); 93                 //判斷是否需要解析 94                 if(nameMap.get(name) != null){ 95                     replacement = parse(jean, jsonPath); 96                 }else{ 97                     //不需要解析直接將屬性值寫為null 98                     replacement = "/""+name+"/": null"; 99                 }100             }else{101                 //無屬性名直接用默認的jean102                 replacement = "/""+defaultName+"/": null";103             }104             105             json = json.replace(jean, replacement);106         }107         108         return json;109     }110     111     /**112      * 解析jean表達式113      * @param express jean表達式114      * @param jsonPath json文件所在路徑,完整路徑115      * @return jean表達式執行結果116      */117     private static String parse(String express,String jsonPath){118         //識別參數119         List<String> params = regexMatchList(paramRegex, express);120         //默認屬性名稱121         String name = defaultName;122         //格式化路徑123         jsonPath = removeSuffix(jsonPath, "/");124         125         //判斷是否傳入了屬性名稱126         if(params.size() > 1){127             name = params.get(0);128         }129         130         //解析路徑131         String path = getAbsolutePath(jsonPath, params.get(params.size()-1));132         133         //讀取內容并返回134         name = wrapWith(name, "/"");135         return name + ": " + readJsonFile(path);136     }137     138     /**139      * 從字符串中移除指定后綴140      * @param source 源字符串141      * @param suffix 需要移除的后綴142      * @return 處理后的源字符串143      */144     private static String removeSuffix(String source,String suffix){145         if(source.endsWith(suffix)){146             source = source.substring(0, source.length()-suffix.length());147         }148         149         return source;150     }151     152     /**153      * list內容去重154      * @param list 內容為string的list155      * @return 內容去重后的list156      */157     private static List<String> noRepeat(List<String> list){158         Map<String, String> map = new HashMap<String, String>();159         List<String> result = new ArrayList<String>();160         161         for(String s : list){162             map.put(s, null);163         }164         165         for(String s : map.keySet()){166             result.add(s);167         }168         169         return result;170     }171     172     /**173      * 用指定的字符串包裹內容174      * @param content 內容175      * @param wrap 包裹字符串176      * @return 包裹后的內容177      */178     private static String wrapWith(String content,String wrap){179         return wrap+content+wrap;180     }181     182     /**183      * 讀取Json文件(純文本文件,utf-8編碼)184      * 這個方法可以替換成自己項目中封裝的方法185      * @param path 文件路徑186      * @return 文件內容187      */188     private static String readJsonFile(String path){189         String encoding = "utf-8";190         StringBuilder sb = new StringBuilder(256);191         192         File file = new File(path);193         InputStreamReader iReader = null;194         BufferedReader bReader = null;195         196         try{197             iReader = new InputStreamReader(new FileInputStream(file), encoding);198             bReader = new BufferedReader(iReader);199             String line = null;200             201             while((line = bReader.readLine()) != null){202                 sb.append(line.trim());203             }204             205             bReader.close();206             iReader.close();207             208         }catch(Exception e){209             if(iReader != null){210                 try {211                     iReader.close();212                 } catch (IOException e1) {213                     iReader = null;214                 }215             }216             if(bReader != null){217                 try {218                     bReader.close();219                 } catch (IOException e1) {220                     bReader = null;221                 }222             }223         }224         225         return sb.toString();226     }227     228     /**229      * 將相對路徑轉換成絕對路徑230      * 只識別 ./ ../231      * @param refrence 基準參照路徑232      * @param relative 相對路徑表達式233      * @return 絕對路徑234      */235     private static String getAbsolutePath(String refrence,String relative){236         if(relative.startsWith("./")){237             refrence = getAbsolutePath(refrence, relative.replaceFirst("http://./", ""));238         }else if(relative.startsWith("../")){239             refrence = getAbsolutePath(refrence.substring(0, refrence.lastIndexOf("/")), 240                             relative.replaceFirst("http://.//./", ""));241         }else{242             refrence = refrence + "/" + relative;243         }244         245         return refrence;246     }247     248     /**249      * 將正則表達式的匹配結果轉換成列表250      * @param regex 正則表達式對象251      * @param input 要檢索的字符串252      * @return 結果列表253      */254     private static List<String> regexMatchList(Pattern regex,String input){255         List<String> result = new ArrayList<String>();256         Matcher matcher = regex.matcher(input);257         while(matcher.find()){258             result.add(matcher.group(1));259         }260         261         return result;262     }263 264 265 }
View Code

其他

歡迎留言,共同探討!


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧洲成人在线视频| 92看片淫黄大片欧美看国产片| 成人h猎奇视频网站| 欧美激情在线有限公司| 日韩av在线免费观看| 尤物99国产成人精品视频| 欧美风情在线观看| 精品亚洲va在线va天堂资源站| 亚洲欧洲免费视频| 亚洲国模精品私拍| 国产欧美日韩中文字幕在线| 欧美午夜影院在线视频| 91精品国产91久久久久久最新| 51精品国产黑色丝袜高跟鞋| 日韩成人高清在线| 国产精品扒开腿做爽爽爽的视频| 亚洲成人999| 午夜精品久久久久久99热| 日韩精品免费看| 欧美成人自拍视频| 91久久精品日日躁夜夜躁国产| 日韩久久精品电影| 68精品国产免费久久久久久婷婷| 黄色成人在线播放| 国产手机视频精品| 国产精品99蜜臀久久不卡二区| 91国语精品自产拍在线观看性色| 上原亚衣av一区二区三区| 亚洲91av视频| 国产97在线观看| 亚洲欧美国产精品| 国产欧美一区二区三区在线| 91成人精品网站| 亚洲精品乱码久久久久久金桔影视| 午夜精品一区二区三区在线视频| 国产亚洲欧洲黄色| 欧美综合第一页| 中文字幕自拍vr一区二区三区| 日韩av在线影院| 欧洲亚洲免费在线| 亚洲成人精品久久| 孩xxxx性bbbb欧美| 91探花福利精品国产自产在线| 久久综合久久八八| 成人亲热视频网站| 日韩中文字幕在线观看| 亚洲欧美日韩爽爽影院| 久久精品99久久久久久久久| 国自在线精品视频| 91精品久久久久久久久久| 久久久久久91| 国产91精品视频在线观看| 国产精品欧美日韩一区二区| 日韩午夜在线视频| 国产在线观看精品一区二区三区| 久久久久久亚洲精品不卡| 成人中文字幕+乱码+中文字幕| 97国产精品久久| 国产91免费观看| 亚洲精品日韩欧美| 久久久久久久久久久国产| 亚洲在线免费视频| 亚洲精品日韩久久久| 久久久99免费视频| 日韩国产欧美区| 91精品视频大全| 国产成人久久久精品一区| 成人有码在线播放| 久久99久久99精品中文字幕| 精品亚洲男同gayvideo网站| 精品久久在线播放| 高清视频欧美一级| 国产精品久久久久7777婷婷| 少妇激情综合网| 国产精品激情av在线播放| 亚洲欧美日韩高清| 欧美在线观看一区二区三区| 91国语精品自产拍在线观看性色| 九九精品在线视频| 美女久久久久久久久久久| 欧美—级a级欧美特级ar全黄| 欧美性猛交xxxx免费看久久久| 亚洲成年人在线播放| 国产精品入口日韩视频大尺度| 久久99青青精品免费观看| 88xx成人精品| 午夜精品免费视频| 国产欧美日韩91| 国产精品一区二区久久精品| 日韩成人中文字幕在线观看| 国产精品久久久久久亚洲调教| 韩国视频理论视频久久| 亚洲色图50p| 亚洲无限乱码一二三四麻| 亚洲欧美精品伊人久久| 国产亚洲精品美女久久久| 亚洲福利在线观看| 欧美一级黑人aaaaaaa做受| 亚洲人av在线影院| 91日本在线视频| 国产剧情日韩欧美| 亚洲无限乱码一二三四麻| 亚洲一区二区三区四区视频| 日韩精品在线免费播放| 亚洲精品成人久久| 欧美精品久久一区二区| 欧美剧在线观看| 精品国产欧美一区二区五十路| 午夜精品久久久久久99热软件| 欧美精品在线播放| 91中文在线视频| 国产视频精品一区二区三区| 一本大道香蕉久在线播放29| 91在线看www| 精品无码久久久久久国产| 亚洲网站在线观看| 亚洲美女又黄又爽在线观看| 欧美亚洲成人精品| 91av视频在线免费观看| 亚洲美女视频网| 亚洲国产精品网站| 久久久女女女女999久久| 欧美天堂在线观看| 国产精品欧美日韩| 国产亚洲一级高清| 亚洲天堂av在线免费观看| 国产欧美韩国高清| 欧美影院久久久| 国产精品天天狠天天看| 欧美日韩国产成人| 久久99国产精品自在自在app| 精品亚洲精品福利线在观看| 精品亚洲永久免费精品| 久久精品亚洲精品| 欧美高清视频免费观看| 欧美日韩成人在线视频| 久久手机精品视频| 九九热在线精品视频| 成人高清视频观看www| 久久久女女女女999久久| 日韩美女视频在线观看| 亚洲色图欧美制服丝袜另类第一页| 岛国视频午夜一区免费在线观看| 精品亚洲一区二区三区在线播放| 日韩电影免费观看中文字幕| 欧美性高跟鞋xxxxhd| 久久男人av资源网站| 亚洲最新在线视频| 久久久久亚洲精品| 国内精品视频一区| 久久99久久99精品免观看粉嫩| 亚洲精品中文字| 亚洲精品福利免费在线观看| 91视频国产一区| 亚洲第一页中文字幕| 97福利一区二区| 高清欧美性猛交xxxx| 91九色视频在线| 欧美不卡视频一区发布| 57pao国产成人免费| 日韩天堂在线视频| 欧美性猛交xxx| 黄色成人在线免费| 国产欧美日韩最新|