snakeyaml支持yml配置文件转map
也支持map转成yml文件
但是有些业务情景下会存在:
yml配置文件转成map后,对<key,value>形式的map的数据进行处理,
比如增加、删除配置,或者修改配置的值,
然后再生成新的yml配置文件,这时导致的问题就是以前的yml文件的注释在新的yml配置文件中都没有了
首先:检查需要引入snakeyaml的依赖
代码:分为三个类
YamlUtils -- 对yml文件操作的工具类
CommentUtils -- 继承YamlUtils,对注解的处理(缓存注解,放入新的yml文件中)
Comment -- 注释对象
YamlUtils
public class YamlUtils {
private static final Logger logger = LoggerFactory.getLogger(YamlUtils.class);
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>) yaml.load(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();
options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
Yaml yaml = new Yaml(options);
yaml.dump(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) {
this.key = key;
}
public String getReplaceKey() {
return replaceKey;
}
public void setReplaceKey(String replaceKey) {
this.replaceKey = replaceKey;
}
public Integer getSort() {
return sort;
}
public void setSort(Integer sort) {
this.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\\application.yml";
String path2 = "C:\\Users\\work\\Hzero\\hzero-helper-parent\\hzero-helper-upgrade\\test.yml";
try {
// 1
map = yml2Map(path1);
buildComment(path1);
// 2
map2Yml(map, path2);
addComment(path2);
} catch (IOException e) {
e.printStackTrace();
}
}*/
public static void buildComment(String path) {
commentList = new ArrayList<>();
Map<String, Integer> keyCountMap = new HashMap<>();
try {
List<String> lines = FileUtils.readLines(new File(path), CHARSET);
for (int i = 0; i < lines.size(); i++) {
String line = lines.get(i);
String tempStr = line.trim();
if (tempStr.startsWith(C)) {
String nextLine = lines.get(i+1);
Comment comment = new Comment();
comment.setKey(nextLine);
comment.setReplaceKey(line + ENTER + nextLine);
comment.setSort(keyCountMap.merge(nextLine, ONE, Integer::sum));
commentList.add(comment);
} else {
if (tempStr.contains(C)) {
String key = (C + StringUtils.substringBefore(line, C)).trim().substring(1);
Comment comment = new Comment();
comment.setKey(key);
comment.setReplaceKey(line);
comment.setSort(keyCountMap.merge(key, ONE, Integer::sum));
commentList.add(comment);
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void addComment(String path) {
try {
String content = FileUtils.readFileToString(new File(path), CHARSET);
for (Comment comment : commentList) {
content = replacePosition(content, comment.getKey(), comment.getReplaceKey(), comment.getSort());
}
FileUtils.writeStringToFile(new File(path), content, CHARSET);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 替换第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 str.replaceFirst(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 = str.indexOf(target);
if (index == -1) {
throw new RuntimeException("Not Found.");
}
index = str.indexOf(target, index + target.length());
if (index == -1) {
throw new RuntimeException("Not Found.");
}
String str1 = str.substring(0, index);
String str2 = str.substring(index);
str2 = str2.replaceFirst(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方法在代码中都有详细的说明