若要轉載本文,請務必聲明出處:https://www.cnblogs.com/zhongyuanzhao000/p/13570432.html
最近要用Java實現一個 將yaml文件轉為properties形式的功能,作為一個老搬運工,肯定先查一查有沒有已經開源的代碼啦,[狗頭]!
發現其實有許多在線轉換工具,比如:https://www.toyaml.com/,這是一個在線yaml與properties互轉的工具,效果很不錯,如果只是偶爾進行文件互轉,可以考慮用這個,優點是簡單便捷。
然而,我需要一段Java代碼來實現 yaml轉 properties的功能。。。
發現真有優秀的大佬分享了 yaml轉properties 的java代碼,感謝大佬的分享,詳見:https://www.cnblogs.com/xujingyang/p/10613206.html。簡單地測試了這份代碼,優點是可以實現基本的轉換功能,但是我的需求比較多,還不能滿足要求。所以,基於這份代碼,我進行了功能完善,支持更多的轉換邏輯。
好了,廢話不多說,下面開搞吧!
注意,由於借鑒了大佬的源碼,所以下面的代碼大體結構都是相似的。
步驟如下:
1、引入yaml轉properties所需的maven依賴
<dependency> <groupId>com.fasterxml.jackson.dataformat</groupId> <artifactId>jackson-dataformat-yaml</artifactId> <version>2.9.4</version> </dependency>
2、工具類如下:
package com.zzy.utils; import com.fasterxml.jackson.core.JsonToken; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import com.fasterxml.jackson.dataformat.yaml.YAMLParser; import java.io.File; import java.io.FileInputStream; import java.io.InputStreamReader; import java.nio.charset.Charset; import java.util.LinkedList; import java.util.List; /** * @author zhongyuanzhao000 * @date 2020/08/27 */ public class FileTranferUtils { /** * tranfer yaml file to properties * @param path the path of yaml file */ public static void yaml2Prop(String path) { List<String> lines = new LinkedList<>(); // DOT用於隔開key中不同的鍵 final String DOT = "*"; //layerOfArray表示當前數組是第幾層數組,默認為0即沒有數組;每遍歷到"["就增加1 int layerOfArray = 0; // inArrays的索引表示yml文件中遍歷到的token位於第幾層數組,而元素內容表示當前遍歷到的token仍在數組內部,元素默認值為false boolean[] inArrays = new boolean[4]; // arrayIndexs的索引表示yml文件中遍歷到的token位於第幾層數組,而元素內容表示其對應的索引,元素默認值為0 int[] arrayIndexs = new int[4]; // arrayCuteds的索引表示yml文件中遍歷到的token位於第幾層數組,而元素內容表示 含有中括號的鍵是否已被切去,元素默認值為false boolean[] arrayCuteds = new boolean[4]; // 注意:上面3個數組,目前均初始化了4個元素值,對應0、1、2、3,表示可以解析最多3層數組嵌套; // 若要更多層,修改該初始值即可 try { YAMLFactory yamlFactory = new YAMLFactory(); YAMLParser parser = yamlFactory.createParser(new InputStreamReader(new FileInputStream(path), Charset.forName("utf-8"))); String key = ""; //這里的key是最終轉換出的properties文件中key,並不是yml文件中的鍵值對中的鍵 String value = null; // 先獲取到基於json格式的第一個token,便於后面的遍歷 JsonToken token = parser.nextToken(); while (token != null) { // 基於json格式,如果是一個對象開始(即遇到了左花括號"{"時) if (JsonToken.START_OBJECT.equals(token)) { // do nothing } else if (JsonToken.FIELD_NAME.equals(token)) { // 基於json格式,如果遇到鍵值對的鍵時 // 使用點"."分割每層的key if (key.length() > 0) { // 如果該對象在數組中,並且key包含中括號的數量不等於 當前所在數組的層次時,則添加上數組索引 if (inArrays[layerOfArray] == true && containNumbers(key, "[") != layerOfArray) { key = key + "[" + arrayIndexs[layerOfArray] + "]"; } key = key + DOT; } key = key + parser.getCurrentName(); // 繼續遍歷下一個token token = parser.nextToken(); /******************************************************************************************/ //如果遇到左中括號"[",表示數組的開始 if (JsonToken.START_ARRAY.equals(token)) { // 進入第一層數組 layerOfArray++; inArrays[layerOfArray] = true; token = parser.nextToken(); } /******************************************************************************************/ // 如果遇到子對象的開始(即"{"),則跳入下一個循環 if (JsonToken.START_OBJECT.equals(token)) { continue; } /******************************************************************************************/ // 此時,當前token遍歷到了一個鍵值對的值時(即到了一個分支盡頭),需要將其放入string數組中 value = parser.getText(); //如果這個值是單獨被包含在中括號數組內(中括號內沒有鍵值對 對應的鍵),則key肯定還沒有在相應的鍵上添加索引,所以這里要補上索引 if (inArrays[layerOfArray] == true && containNumbers(key, "[") != layerOfArray) { key = key + "[" + arrayIndexs[layerOfArray] + "]"; } lines.add(key + "=" + value); /******************************************************************************************/ // 每當遍歷完一個分支,需要將 key截斷到倒數第二個鍵 int dotOffset = key.lastIndexOf(DOT); if (key.length() - 1 == key.lastIndexOf("]")) { arrayCuteds[layerOfArray] = true; } if (dotOffset > 0) { key = key.substring(0, dotOffset); } else { // 若原key中沒有".",則key直接置為"" key = ""; } // 若截斷后剩下的key的最后一個鍵含有中括號,也就是該鍵的索引即將更新,則去除掉該中括號子串以便於后面添加新的索引 if (key.length() > 0 && key.length() - 1 == key.lastIndexOf("]")) { key = key.substring(0, key.lastIndexOf("[")); } }else if (JsonToken.END_OBJECT.equals(token)) { // 基於json格式,如果是一個對象結束(即遇到了右花括號"}"時) // 如果當前token在數組內部,則不需要截斷 if (inArrays[layerOfArray]) { arrayIndexs[layerOfArray]++; } else { int dotOffset = key.lastIndexOf(DOT); if (dotOffset > 0) { // 若原key中還有".",則截斷到倒數第二個鍵 key = key.substring(0, dotOffset); } else { // 若原key中沒有".",則key直接置為"" key = ""; } } } else if (JsonToken.END_ARRAY.equals(token)) { //如果遇到右中括號"]",表示數組的結束 // 若當前層次中 含有中括號的鍵未被切去 if (!arrayCuteds[layerOfArray]) { int dotOffset = key.lastIndexOf(DOT); if (dotOffset > 0) { // 若原key中還有".",則截斷到倒數第二個鍵 key = key.substring(0, dotOffset); } else { // 若原key中沒有".",則key直接置為"" key = ""; } } // 重置該層的變量 inArrays[layerOfArray] = false; arrayIndexs[layerOfArray] = 0; arrayCuteds[layerOfArray] = false; // 回退到上一層 layerOfArray--; // 若截斷后剩下的key的最后一個鍵含有中括號,也就是上一層中 該鍵的索引即將更新,則去除掉該中括號子串 以便於后面添加新的索引 if (key.length() > 0 && key.length() - 1 == key.lastIndexOf("]")) { key = key.substring(0, key.lastIndexOf("[")); } } token = parser.nextToken(); } parser.close(); // 將String字符串數組中的內容打印出來 for (String line : lines) { line = line.replace(DOT, "."); System.out.println(line); } } catch (Exception e) { throw new RuntimeException(e); } } /** * 計算 字符串str中包含多少個 模式串s * @param str 主字符串 * @param s 模式串 * @return */ private static int containNumbers(String str, String s) { int count = 0; for(int i = 0; i < str.length(); ){ int c = -1; c = str.indexOf(s); if (c != -1){ str = str.substring(c + 1); count ++; } else { break; } } return count; } }
使用yaml2Prop方法,指定yml文件路徑就可以輸出properties內容在控制台上了。
如果需要了解該方法的代碼,建議先參考jackson倉庫,然后將yml文件內容轉為json形式,再根據json形式來理解方法代碼。
最后,如果要檢驗該方法的效果,可以使用在線yaml轉properties工具,然后將工具轉換結果 與 該方法結果比較即可。