1. 程式人生 > >Java Web:JSON 作為配置檔案,簡單讀寫的方法

Java Web:JSON 作為配置檔案,簡單讀寫的方法

讀取配置檔案

先大概說一說思路。首先配置檔案以 *.json 格式儲存在服務端磁碟上。要讀取改配置檔案的話,通過 java.io.File 包讀取磁碟內容,然後形成介面,作為響應內容返回到客戶。既然 Web 瀏覽器天然支援 JSON,這讀取一過程我們藉助 <script src="xxx.json"></script> 即可。得到 JSON 資料後,再通過 JavaScript 繫結到 HTML 表單上。

讀取 JSON 檔案內容很簡單,我的程式碼如下:

String filePath = new RequestHelper(request).Mappath("/META-INF/site_config.js");
out.println(Fso.readFile(filePath));

RequestHelper.Mappath 和 Fso.readFile 都是我封裝的函式,分別是把虛擬的相對路徑還原為磁碟的絕對路徑;Fso.readFile 顧名思義是讀取檔案內容。你可以將此寫成 JSP 或 Servlet,讓瀏覽器通過 <script src="xxx.jsp"></script> 訪問得到即可。另外這裡我還加入了格式化函式(Formatter),讓輸出的 JSON 帶有縮排,更方便除錯和閱讀。

<script type="text/framework/javascript" src="?getConfig"></script>
如上例,我是設定當前 JSP 得到 JSON 內容的,效果如下圖。

注意我們把 JSON 顯示到表單是通過下面簡單的函式的:

// 資料繫結
// v1.00
function databing(data){
	for(var i in data){
		var el = document.querySelector('*[name="bf_Config.site.{0}"]'.format(i));
		el.value = data[i];
	}
}
databing(bf_Config.site);
這裡表單各個輸入控制元件的 name 屬性是有講究的,命名方式都是以 JSON 的中 JSON Path 完整路徑為依據,如這裡的 bf_Config.site.{0}。一個控制元件項對應一個 JSON 節點。

表單程式碼如下:

<form class="form-horizontal" method="post" action="?">
	<input type="hidden" name="jsonFile" value="/META-INF/site_config.js" />
	<input type="hidden" name="topVarName" value="bf_Config" />
	<div class="control-group">
		<label class="control-label">網站標題字首</label>
		<div class="controls">
			<input type="text" name="bf_Config.site.titlePrefix" class="ui-wizard-content"
				placeholder="請輸入舊密碼" errmsg="舊密碼為必填項" requiredfield />
		</div>
	</div>
	<div class="control-group">
		<label class="control-label">搜尋關鍵字</label>
		<div class="controls">
			<textarea name="bf_Config.site.keywords" rows="10"></textarea>
			<span class="requiredfield">*</span>
		</div>
	</div>
	<div class="control-group">
		<label class="control-label">網站描述</label>
		<div class="controls">
			<textarea name="bf_Config.site.description" rows="10"></textarea>
			<span class="requiredfield">*</span>
		</div>
	</div>
	<div class="control-group">
		<label class="control-label">底部版權<br />宣告文字
		</label>
		<div class="controls">
			<textarea name="bf_Config.site.footCopyright" rows="10"></textarea>
			<span class="requiredfield">*</span>
		</div>
	</div>
	<div class="form-actions">
		<button class="btn btn-success submitBtn">儲存</button>
	</div>
</form>

生成表單介面如下:

其中要注意兩處隱藏域:

<input type="hidden" name="jsonFile" value="/META-INF/site_config.js" />
<input type="hidden" name="topVarName" value="bf_Config" />
一個說明是哪個 JSON 檔案要被儲存的,一個是 JSON 的頂級節點名。

儲存配置內容

點選“儲存”按鈕之後,表單提交如下資料到後臺。

儲存配置檔案,一個是修改當前記憶體的配置資訊,其次是將配置記憶體儲存在服務端的磁碟上,所以說這裡是兩個主要的操作,因此也需要兩個 JavaScript 執行時。一個是 BaseApplication.jsRuntime 靜態型別的,常駐記憶體的,另外一個是用於當前請求的例項化的這麼一個 JS 執行時,用於得到 JSON 序列化後的字串。它們分別作用各不同。

以上邏輯都安排在 save(HttpServletRequest request) 方法:

private void save(HttpServletRequest request) throws Exception {
	Map<String, String> hash = RequestSender.getClient_Data(request);
	String jsonFileFullPath = load(request, hash);
	
	// 可以json.str() 的 變數名
	String topVarName = hash.get("topVarName");
	Util.isEmptyString(topVarName, "沒有  topVarName 引數!");
	hash.remove("topVarName");
        saveRAM(hash);
	
	String JSON_as_String = null;
	try {
		JSON_as_String = (String)eval("JSON.stringify(" + topVarName + ");");
	} catch (ScriptException e) {
		e.printStackTrace();
		throw new Exception("更新配置失敗,不能序列化配置!");
	}
	
	if(JSON_as_String != null){ // 持久化配置檔案
		String fileBody = topVarName + " = " + JSON_as_String + ";";
//			System.out.println(fileBody);
//			System.out.println("::::::::::::::::::2:"+jsonFileFullPath);
		try {
			Fso.writeFile(jsonFileFullPath, fileBody);
		} catch (IOException e) {
			e.printStackTrace();
			throw new Exception("更新配置失敗,不能儲存配置!");
		}
	}
}

首先,我們需要把這些資料變為一個 Map:Map<String, String> hash = RequestSender.getClient_Data(request);,載入到記憶體中,然後對其修改(實際是覆蓋過程,同時對兩個 js runtime 皆有效),最後儲存到檔案中(Fso.write)。

/**
 * 寫入記憶體,覆蓋
 * @param hash
 */
private void save(Map<String, String> hash){
	String jsCode = "";
	
	for(String key : hash.keySet()){
		jsCode = key + " = '" + hash.get(key) + "';"; // 全部儲存為 String。TODO 支援其他型別
		
//			System.out.println(jsCode);
		
		try{
			js.eval(jsCode); // 兩個 js runtime
			eval(jsCode);
		}catch(ScriptException e) {
			System.err.println("寫入記憶體,覆蓋失敗!");
			e.printStackTrace();
		}
	}
}

最後儲存完畢,輸出 JSON 結果:new ResponseHelper(response).outputJSON(request); 提示使用者成功。應該說整個過程並不複雜,操作也足夠直觀。如果要說有什麼地方沒考慮到的,就是安全性了。實際使用中還需要注意許可權,因為這是直接對服務端的檔案進行寫操作!

另外有一點可以優化的地方,那就是合併兩個 js runtime 為一個,不知是否可行呢?