javascript代码实例教程-服务器端json数据文件分割合并解决方案

发布时间:2019-01-24 发布网站:脚本宝典
脚本宝典收集整理的这篇文章主要介绍了javascript代码实例教程-服务器端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//((/"[^/"]*/",)?/"[^/"]*/"//))");

 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                 //无属性名直接用默认的jean

102                 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的list

155      * @return 内容去重后的list

156      */

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("//./", ""));

238         }else if(relative.startsWith("../")){

239             refrence = getAbsolutePath(refrence.substring(0, refrence.lastIndexOf("/")), 

240                             relative.replaceFirst("//.//./", ""));

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 }

觉得可用,就经常来吧! 脚本宝典 欢迎评论哦! js脚本,巧夺天工,精雕玉琢。小宝典献丑了!

脚本宝典总结

以上是脚本宝典为你收集整理的javascript代码实例教程-服务器端json数据文件分割合并解决方案全部内容,希望文章能够帮你解决javascript代码实例教程-服务器端json数据文件分割合并解决方案所遇到的问题。

如果觉得脚本宝典网站内容还不错,欢迎将脚本宝典推荐好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。