Yml轉(zhuǎn)properties文件工具類YmlUtils的詳細(xì)過程(不用引任何插件和依賴)
【誕生背景】
最近在做某配置中心的時(shí)候,配置中心采用properties格式進(jìn)行配置的(如下圖)。
而我們工程的項(xiàng)目配置文件是yml格式的(如下圖)。
如果人為手動(dòng)的一條一條,將yml文件中的配置數(shù)據(jù),添加到配置中心,難免會消耗大量的人力和精力,況且還容易輸入錯(cuò)誤。因此,需要一個(gè)工具或插件,將 yml 文件的格式,轉(zhuǎn)換為properties文件。
【Convert YAML and Properties File 插件的不足】
IDEA 有一個(gè)插件叫 Convert YAML and Properties File, 于是,首先用了一下 這個(gè)插件后,發(fā)現(xiàn)了,發(fā)現(xiàn)這個(gè)插件不太友好,具體有以下幾點(diǎn)。
比如,現(xiàn)在我們有如下的 yml 配置文件:
我們用插件將它轉(zhuǎn)化為 properties 文件。
下面是轉(zhuǎn)化后的效果:
從這轉(zhuǎn)換后的效果,我們不難發(fā)現(xiàn)該插件有以下幾點(diǎn)問題:
(1)轉(zhuǎn)化后,原 yml 配置文件消失(如果轉(zhuǎn)出了問題,想看原配置文件,還看不了了);
(2)排序出現(xiàn)混亂,沒有按照原 yml 文件數(shù)據(jù)進(jìn)行輸出(msg相關(guān)的配置本來在原yml文件中是第二個(gè)配置,轉(zhuǎn)換后卻成為了第一個(gè);同理,mybatis的配置本是最后一個(gè),轉(zhuǎn)化后卻放在了第二個(gè));
(3)所有注釋均不見了(所有相關(guān)的注釋全都不見了,包括行級注釋和末尾注釋);
(4)某些值沒有進(jìn)行配置,但轉(zhuǎn)化后,卻顯示為了 null 字符串(如 msg.sex 的配置);
(5)該插件僅IDEA有,Eclipse中還沒有,不能垮開發(fā)工具使用;
【自寫小工具 YmlUtils 實(shí)現(xiàn)】
針對上面 IDEA 插件的不足,于是自己寫了一款小工具 YmlUtils(源碼在文章結(jié)尾處 ),你可以將它放在工程中的任何位置。
現(xiàn)在,我們同樣以剛剛的 yml 配置文件為測試模板,來測試下這款小工具。
測試的方法很簡單,只需要將 yml 配置文件放在根目錄下,然后寫個(gè) Test 類,調(diào)用里面的 castProperties 方法即可。
YmlUtils.castProperties("application-test.yml");
執(zhí)行方法后,首先我們可以看到控制臺會將 porperties 文件的內(nèi)容打印到控制臺上面:
程序運(yùn)行完成后,根目錄下會多出一個(gè)與yml同名的properties文件,該文件就直接拷貝到相應(yīng)的地方進(jìn)行使用,而且原文件也并未收到任何損壞和影響。
【源碼展示】
最后附上工具類源碼,如果對你有用,記得一鍵三連(支持原創(chuàng))。
package com.test.utils; import java.io.File; import java.io.FileInputStream; import java.io.FileWriter; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.*; /** * Yaml 配置文件轉(zhuǎn) Properties 配置文件工具類 * @author https://zyqok.blog.csdn.net/ * @since 2021/08/24 */ public class YmlUtils { /** * 將 yml 文件轉(zhuǎn)化為 properties 文件 * * @param ymlFileName 工程根目錄下(非resources目錄)的 yml 文件名稱(如:abc.yml) * @return List<Node> 每個(gè)Nyml 文件中每行對應(yīng)解析的數(shù)據(jù) */ public static List<YmlNode> castProperties(String ymlFileName) { if (ymlFileName == null || ymlFileName.isEmpty() || !ymlFileName.endsWith(".yml")) { throw new RuntimeException("請輸入yml文件名稱?。?); } File ymlFile = new File(ymlFileName); if (!ymlFile.exists()) { throw new RuntimeException("工程根目錄下不存在 " + ymlFileName + "文件??!"); } String fileName = ymlFileName.split(".yml", 2)[0]; // 獲取文件數(shù)據(jù) String yml = read(ymlFile); List<YmlNode> nodeList = getNodeList(yml); // 去掉多余數(shù)據(jù),并打印 String str = printNodeList(nodeList); // 將數(shù)據(jù)寫入到 properties 文件中 String propertiesName = fileName + ".properties"; File file = new File(propertiesName); if (!file.exists()) { try { file.createNewFile(); } catch (IOException e) { e.printStackTrace(); } } try (FileWriter writer = new FileWriter(file)) { writer.write(str); writer.flush(); } catch (IOException e) { e.printStackTrace(); } return nodeList; } /** * 將yml轉(zhuǎn)化為porperties文件,并獲取轉(zhuǎn)化后的鍵值對 * * @param ymlFileName 工程根目錄下的 yml 文件名稱 * @return 轉(zhuǎn)化后的 porperties 文件鍵值對Map */ public static Map<String, String> getPropertiesMap(String ymlFileName) { Map<String, String> map = new HashMap<>(); List<YmlNode> list = castProperties(ymlFileName); for (YmlNode node : list) { if (node.getKey().length() > 0) { map.put(node.getKey(), node.getValue()); } } return map; } private static String read(File file) { if (Objects.isNull(file) || !file.exists()) { return ""; } try (FileInputStream fis = new FileInputStream(file)) { byte[] b = new byte[(int) file.length()]; fis.read(b); return new String(b, StandardCharsets.UTF_8); } catch (IOException e) { e.printStackTrace(); } return ""; } private static String printNodeList(List<YmlNode> nodeList) { StringBuilder sb = new StringBuilder(); for (YmlNode node : nodeList) { if (node.getLast().equals(Boolean.FALSE)) { continue; } if (node.getEmptyLine().equals(Boolean.TRUE)) { System.out.println(); sb.append("\r\n"); continue; } // 判斷是否有行級注釋 if (node.getHeadRemark().length() > 0) { String s = "# " + node.getHeadRemark(); System.out.println(s); sb.append(s).append("\r\n"); continue; } // 判斷是否有行末注釋 (properties中注釋不允許末尾注釋,故而放在上面) if (node.getTailRemark().length() > 0) { String s = "# " + node.getTailRemark(); System.out.println(s); sb.append(s).append("\r\n"); } // String kv = node.getKey() + "=" + node.getValue(); System.out.println(kv); sb.append(kv).append("\r\n"); } return sb.toString(); } private static List<YmlNode> getNodeList(String yml) { String[] lines = yml.split("\r\n"); List<YmlNode> nodeList = new ArrayList<>(); Map<Integer, String> keyMap = new HashMap<>(); Set<String> keySet = new HashSet<>(); for (String line : lines) { YmlNode node = getNode(line); if (node.getKey() != null && node.getKey().length() > 0) { int level = node.getLevel(); if (level == 0) { keyMap.clear(); keyMap.put(0, node.getKey()); } else { int parentLevel = level - 1; String parentKey = keyMap.get(parentLevel); String currentKey = parentKey + "." + node.getKey(); keyMap.put(level, currentKey); node.setKey(currentKey); } } keySet.add(node.getKey() + "."); nodeList.add(node); } // 標(biāo)識是否最后一級 for (YmlNode each : nodeList) { each.setLast(getNodeLast(each.getKey(), keySet)); } return nodeList; } private static boolean getNodeLast(String key, Set<String> keySet) { if (key.isEmpty()) { return true; } key = key + "."; int count = 0; for (String each : keySet) { if (each.startsWith(key)) { count++; } } return count == 1; } private static YmlNode getNode(String line) { YmlNode node = new YmlNode(); // 初始化默認(rèn)數(shù)據(jù)(防止NPE) node.setEffective(Boolean.FALSE); node.setEmptyLine(Boolean.FALSE); node.setHeadRemark(""); node.setKey(""); node.setValue(""); node.setTailRemark(""); node.setLast(Boolean.FALSE); node.setLevel(0); // 空行,不處理 String trimStr = line.trim(); if (trimStr.isEmpty()) { node.setEmptyLine(Boolean.TRUE); return node; } // 行注釋,不處理 if (trimStr.startsWith("#")) { node.setHeadRemark(trimStr.replaceFirst("#", "").trim()); return node; } // 處理值 String[] strs = line.split(":", 2); // 拆分后長度為0的,屬于異常數(shù)據(jù),不做處理 if (strs.length == 0) { return node; } // 獲取鍵 node.setKey(strs[0].trim()); // 獲取值 String value; if (strs.length == 2) { value = strs[1]; } else { value = ""; } // 獲取行末備注 String tailRemark = ""; if (value.contains(" #")) { String[] vs = value.split("#", 2); if (vs.length == 2) { value = vs[0]; tailRemark = vs[1]; } } node.setTailRemark(tailRemark.trim()); node.setValue(value.trim()); // 獲取當(dāng)前層級 int level = getNodeLevel(line); node.setLevel(level); node.setEffective(Boolean.TRUE); return node; } private static int getNodeLevel(String line) { if (line.trim().isEmpty()) { return 0; } char[] chars = line.toCharArray(); int count = 0; for (char c : chars) { if (c != ' ') { break; } count++; } return count / 2; } } class YmlNode { /** 層級關(guān)系 */ private Integer level; /** 鍵 */ private String key; /** 值 */ private String value; /** 是否為空行 */ private Boolean emptyLine; /** 當(dāng)前行是否為有效配置 */ private Boolean effective; /** 頭部注釋(單行注釋) */ private String headRemark; /** 末尾注釋 */ private String tailRemark; /** 是否為最后一層配置 */ private Boolean last; public Boolean getLast() { return last; } public void setLast(Boolean last) { this.last = last; } public Integer getLevel() { return level; } public void setLevel(Integer level) { this.level = level; } public String getKey() { return key; } public void setKey(String key) { this.key = key; } public String getValue() { return value; } public void setValue(String value) { this.value = value; } public Boolean getEmptyLine() { return emptyLine; } public void setEmptyLine(Boolean emptyLine) { this.emptyLine = emptyLine; } public Boolean getEffective() { return effective; } public void setEffective(Boolean effective) { this.effective = effective; } public String getHeadRemark() { return headRemark; } public void setHeadRemark(String headRemark) { this.headRemark = headRemark; } public String getTailRemark() { return tailRemark; } public void setTailRemark(String tailRemark) { this.tailRemark = tailRemark; } }
到此這篇關(guān)于Yml轉(zhuǎn)properties文件工具類YmlUtils(不用引任何插件和依賴)的文章就介紹到這了,更多相關(guān)Yml轉(zhuǎn)properties文件工具類YmlUtils內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
六個(gè)Java集合使用時(shí)需要注意的事項(xiàng)
這篇文章主要為大家詳細(xì)介紹了六個(gè)Java集合使用時(shí)需要注意的事項(xiàng),文中的示例代碼講解詳細(xì),對我們學(xué)習(xí)java有一定的幫助,需要的可以參考一下2023-01-01java觀察者模式實(shí)現(xiàn)和java觀察者模式演化
觀察者模式是經(jīng)典設(shè)計(jì)模式中很常用的一種,平常我們看到的監(jiān)聽器,基本上都是采用這種設(shè)計(jì)模式實(shí)現(xiàn)的,這里探討一下觀察者模式的演化2014-02-02Springboot如何添加server.servlet.context-path相關(guān)使用
這篇文章主要介紹了Springboot如何添加server.servlet.context-path相關(guān)使用問題,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-03-03java設(shè)計(jì)模式之裝飾模式詳細(xì)介紹
這篇文章主要介紹了java設(shè)計(jì)模式之裝飾模式,有需要的朋友可以參考一下2013-12-12Spring Cloud微服務(wù)使用webSocket的方法
WebSocket在現(xiàn)代瀏覽器中的應(yīng)用已經(jīng)算是比較普遍了,在某些業(yè)務(wù)場景下,要求必須能夠在服務(wù)器端推送消息至客戶端,本文給大家介紹Spring Cloud微服務(wù)使用webSocket的方法,感興趣的朋友一起看看吧2021-06-06提交gRPC-spring-boot-starter項(xiàng)目bug修復(fù)的pr說明
這篇文章主要介紹了這篇文章主要為大家介紹了gRPC-spring-boot-starter項(xiàng)目提交bug修復(fù)的pr的原因說明,有需要的朋友可以借鑒參考下,希望能夠有所幫助2022-02-02Java 靜態(tài)數(shù)據(jù)初始化的示例代碼
這篇文章主要介紹了Java 靜態(tài)數(shù)據(jù)初始化的示例代碼,幫助大家更好的理解和學(xué)習(xí)Java,感興趣的朋友可以了解下2020-09-09Maven多模塊及version修改的實(shí)現(xiàn)方法
這篇文章主要介紹了Maven多模塊及version修改的實(shí)現(xiàn)方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-06-06