情景
snakeyaml支持yml配置文件转map
也支持map转成yml文件
但是有些业务情景下会存在:
yml配置文件转成map后,对<key,value>形式的map的数据进行处理,
比如增加、删除配置,或者修改配置的值,
然后再生成新的yml配置文件,这时导致的问题就是以前的yml文件的注释在新的yml配置文件中都没有了
解决办法
- 一开始考虑查看snakeyaml的源码,尝试覆盖一些源码的类去解决,能力有限,没尝试成功。
- 只能想到对文件进行处理了,对一开始的yml文件中的注释进行缓存,再生成新的yml文件后再把缓存好的注释正确的放进去
- 问题:如何缓存,如何正确的放入新的yml中?
- 个人方法:
首先:检查需要引入snakeyaml的依赖
代码:分为三个类
YamlUtils -- 对yml文件操作的工具类
CommentUtils -- 继承YamlUtils,对注解的处理(缓存注解,放入新的yml文件中)
Comment -- 注释对象
YamlUtils
public class YamlUtils {
private static final Logger logger = ();
public static Map<String, Object> yml2Map(String path) throws FileNotFoundException {
FileInputStream fileInputStream = new FileInputStream(path);
Yaml yaml = new Yaml();
Map<String, Object> ret = (Map<String, Object>) (fileInputStream);
return ret;
}
public static void map2Yml(Map<String, Object> map, String path) throws IOException {
File file = new File(path);
FileWriter fileWriter = new FileWriter(file);
DumperOptions options = new DumperOptions();
();
Yaml yaml = new Yaml(options);
(map, fileWriter);
}
}
Comment
public class Comment {
/**
* 替换前的
*/
private String key;
/**
* 替换后的
*/
private String replaceKey;
/**
* 位置(默认为1,一般替换前的行内容(key)都不一样):若存在替换前的行内容(key)一致,则需此参数
*/
private Integer sort;
public String getKey() {
return key;
}
public void setKey(String key) {
= key;
}
public String getReplaceKey() {
return replaceKey;
}
public void setReplaceKey(String replaceKey) {
= replaceKey;
}
public Integer getSort() {
return sort;
}
public void setSort(Integer sort) {
= sort;
}
}
CommentUtils
class CommentUtils extends YamlUtils {
private static volatile List<Comment> commentList;
private final static String C = "#";
private final static Integer ONE = 1;
private final static String ENTER = "\r\n";
private final static String CHARSET = "UTF-8";
/*public static void main(String[] args) {
Map<String, Object> map = null;
String path1 = "C:\\Users\\work\\Hzero\\hzero-generator\\src\\main\\resources\\";
String path2 = "C:\\Users\\work\\Hzero\\hzero-helper-parent\\hzero-helper-upgrade\\";
try {
// 1
map = yml2Map(path1);
buildComment(path1);
// 2
map2Yml(map, path2);
addComment(path2);
} catch (IOException e) {
();
}
}*/
public static void buildComment(String path) {
commentList = new ArrayList<>();
Map<String, Integer> keyCountMap = new HashMap<>();
try {
List<String> lines = (new File(path), CHARSET);
for (int i = 0; i < (); i++) {
String line = (i);
String tempStr = ();
if ((C)) {
String nextLine = (i+1);
Comment comment = new Comment();
(nextLine);
(line + ENTER + nextLine);
((nextLine, ONE, Integer::sum));
(comment);
} else {
if ((C)) {
String key = (C + (line, C)).trim().substring(1);
Comment comment = new Comment();
(key);
(line);
((key, ONE, Integer::sum));
(comment);
}
}
}
} catch (IOException e) {
();
}
}
public static void addComment(String path) {
try {
String content = (new File(path), CHARSET);
for (Comment comment : commentList) {
content = replacePosition(content, (), (), ());
}
(new File(path), content, CHARSET);
} catch (IOException e) {
();
}
}
/**
* 替换第position个匹配的目标子串
* @param str 源字符串。
* @param target 需要替换的目标子串。
* @param replacement 需要替换成的字符串。
* @return 将源字符串中出现的第n个target换成replacement后的字符串。
*/
public static String replacePosition(String str, String target, String replacement, int position) {
if (str == null || target == null || replacement == null) {
throw new NullPointerException();
}
if (position < 2) {
return (target, replacement);
}
String result = str;
for (int i = 1; i < position; i++) {
result = replaceSecond(result, target, replacement);
}
return result;
}
/**
* 替换第二个匹配的目标子串
* @param str 源字符串。
* @param target 需要替换的目标子串。
* @param replacement 需要替换成的字符串。
* @return 将源字符串中出现的第二个target换成replacement后的字符串。
*/
public static String replaceSecond(String str, String target, String replacement){
if (str == null || target == null || replacement == null) {
throw new NullPointerException();
}
int index = (target);
if (index == -1) {
throw new RuntimeException("Not Found.");
}
index = (target, index + ());
if (index == -1) {
throw new RuntimeException("Not Found.");
}
String str1 = (0, index);
String str2 = (index);
str2 = (target, replacement);
return str1 + str2;
}
}
代码及其注释都很简单明了,稍微解释下之前第三点提的问题
-
buildComment方法去缓存一开始的yml文件中的注释
key – 表示带有注释的那一行的实际内容
replaceKey – 要替换key的内容
sort – key的顺序
比如:# 服务端口 server: port: ${HGEN_SERVER_PORT:8265} management: # actuator监控端口 server: port: ${HGEN_MANAGEMENT_SERVER_PORT:8266} endpoints: web: exposure: include: '*' #这是注释
举例:
一:注释在头上(1)# 服务端口 server:
此时,
key为server:
replaceKey为
# 服务端口 server:
sort为默认值1,因为整个文件中key为
server:
的只有这一行
二:注释在头上(2)
# actuator监控端口 server:
此时,
key为server:
replaceKey为
# actuator监控端口 server:
sort为默认值1,因为整个文件中key为
server:
的只有这一行
(注意看空格,因为存在空格所以上面两个key是不一样的,如果存在一样的key时,后面的key的sort会按顺序累加)三:注释在后面
include: '*' #这是注释
此时,
key为include: '*'
replaceKey为
include: '*' #这是注释
sort为默认值1,因为整个文件中key为
include: '*'
的只有这一行
-
addComment方法中把生成好的yml文件读成String,再循环缓存好的注释Comment对象List,通过replacePosition方法把带注释的内容替换进去,再写成文件。replacePosition和replaceSecond方法在代码中都有详细的说明