小企业网站源码,xml格式文件打开都是乱码,长沙网站空间,西宁做网站君博推荐文章目录 前言技术积累实战演示1、引入maven依赖2、覆盖注释工具类3、snakeyaml工具类4、测试用例5、测试效果展示 写在最后 前言
最近在做一个动态整合框架的项目#xff0c;需要根据需求动态组装各个功能模块。其中就涉及到了在application.yaml中加入其他模块的配置#… 文章目录 前言技术积累实战演示1、引入maven依赖2、覆盖注释工具类3、snakeyaml工具类4、测试用例5、测试效果展示 写在最后 前言
最近在做一个动态整合框架的项目需要根据需求动态组装各个功能模块。其中就涉及到了在application.yaml中加入其他模块的配置这里我们采用了snakeyaml进行配置信息写入并采用文件回写保证注释不丢失。
技术积累 SnakeYaml就是用于解析YAML序列化以及反序列化的第三方框架解析yml的三方框架有很多SnakeYamljYamlJackson等但是不同的工具功能还是差距较大比如jYaml就不支持合并。 SnakeYaml是一个完整的YAML1.1规范Processor支持UTF-8/UTF-16支持Java对象的序列化/反序列化支持所有YAML定义的类型。 SnakeYaml官方地址http://yaml.org/type/index.html 实战演示
1、引入maven依赖
!--yaml编辑--
dependencygroupIdorg.yaml/groupIdartifactIdsnakeyaml/artifactIdversion1.23/version
/dependency
dependencygroupIdcommons-io/groupIdartifactIdcommons-io/artifactIdversion2.11.0/version
/dependency
dependencygroupIdorg.apache.commons/groupIdartifactIdcommons-lang3/artifactIdversion3.9/version
/dependency2、覆盖注释工具类 由于snakeyaml在操作文件时候会先将yaml转为map然后再回写到文件这个操作会导致注释丢失。 目前有效的方案是将修改前文件注释进行缓存然后当业务操作完文件后进行注释会写这样就能够保证注释不会被覆盖。 当然目前的方案并没有增加新的配置文件注释写入功能有需要的同学可以自己实现。大概的思路是根据在回写注释的时候根据key将新增的注释写入此时需要注释多个key相同的情况故需要判断全链路key以防止重复注释乱序。 package com.example.demo.utils;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.SneakyThrows;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;/*** CommentUtils* author senfel* version 1.0* date 2023/12/6 18:20*/
public class CommentUtils {public static final String END END###;public static final Pattern COMMENT_LINE Pattern.compile(^\\s*#.*$);public static final Pattern BLANK_LINE Pattern.compile(^\\s*$);//带注释的有效行, 使用非贪婪模式匹配有效内容public static final Pattern LINE_WITH_COMMENT Pattern.compile(^(.*?)\\s#.*$);DataAllArgsConstructorpublic static class Comment {private String lineNoComment;private String lineWithComment;private Integer indexInDuplicates; // 存在相同行时的索引 (不同key下相同的行, 如 a:\n name: 1 和 b:\n name: 1 )private boolean isEndLine() {return END.equals(lineNoComment);}}SneakyThrowspublic static CommentHolder buildCommentHolder(File file) {ListComment comments new ArrayList();MapString, Integer duplicatesLineIndex new HashMap();CommentHolder holder new CommentHolder(comments);ListString lines FileUtils.readLines(file, StandardCharsets.UTF_8);// 末尾加个标志, 防止最后的注释丢失lines.add(END);StringBuilder lastLinesWithComment new StringBuilder();for (String line : lines) {if (StringUtils.isBlank(line) || BLANK_LINE.matcher(line).find()) {lastLinesWithComment.append(line).append(\n);continue;}// 注释行/空行 都拼接起来if (COMMENT_LINE.matcher(line).find()) {lastLinesWithComment.append(line).append(\n);continue;}String lineNoComment line;boolean lineWithComment false;// 如果是带注释的行, 也拼接起来, 但是记录非注释的部分Matcher matcher LINE_WITH_COMMENT.matcher(line);if (matcher.find()) {lineNoComment matcher.group(1);lineWithComment true;}// 去除后面的空格lineNoComment lineNoComment.replace(\\s*$, );// 记录下相同行的索引Integer idx duplicatesLineIndex.merge(lineNoComment, 1, Integer::sum);// 存在注释内容, 记录if (lastLinesWithComment.length() 0 || lineWithComment) {lastLinesWithComment.append(line);comments.add(new Comment(lineNoComment, lastLinesWithComment.toString(), idx));// 清空注释内容lastLinesWithComment new StringBuilder();}}return holder;}AllArgsConstructorpublic static class CommentHolder {private ListComment comments;/*** 通过正则表达式移除匹配的行 (防止被移除的行携带注释信息, 导致填充注释时无法正常匹配)*/public void removeLine(String regex) {comments.removeIf(comment - comment.getLineNoComment().matches(regex));}/*** fillComments* param file* author senfel* date 2023/12/7 11:24* return void*/SneakyThrowspublic void fillComments(File file) {if (comments null || comments.isEmpty()) {return;}if (file null || !file.exists()) {throw new IllegalArgumentException(file is not exist);}ListString lines FileUtils.readLines(file, StandardCharsets.UTF_8);MapString, Integer duplicatesLineIndex new HashMap();int comIdx 0;StringBuilder res new StringBuilder();for (String line : lines) {Integer idx duplicatesLineIndex.merge(line, 1, Integer::sum);Comment comment getOrDefault(comments, comIdx, null);if (comment ! null Objects.equals(line, comment.lineNoComment) Objects.equals(comment.indexInDuplicates, idx)) {res.append(comment.lineWithComment).append(\n);comIdx;} else {res.append(line).append(\n);}}Comment last comments.get(comments.size() - 1);if (last.isEndLine()) {res.append(last.lineWithComment.substring(0, last.lineWithComment.indexOf(END)));}FileUtils.write(file, res.toString(), StandardCharsets.UTF_8);}}public static T T getOrDefault(ListT vals, int index, T defaultVal) {if (vals null || vals.isEmpty()) {return defaultVal;}if (index vals.size()) {return defaultVal;}T v vals.get(index);return v null ? defaultVal : v;}}3、snakeyaml工具类 snakeyaml工具类主要作用就是将yaml文件转为map的格式然后依次进行判断写入或者修改value。 package com.example.demo.utils;import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.util.HashMap;
import java.util.Map;/*** YamlActionUtils * author senfel* version 1.0* date 2023/12/7 13:48*/
public class YamlActionUtils {/*** 配置* author senfel* date 2023/12/7 13:49* return*/private static DumperOptions dumperOptions new DumperOptions();static{//设置yaml读取方式为块读取dumperOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);dumperOptions.setDefaultScalarStyle(DumperOptions.ScalarStyle.PLAIN);dumperOptions.setPrettyFlow(false);}/*** insertYaml* param key a.b.c* param value* param path* author senfel* date 2023/12/7 10:11* return boolean*/public static boolean insertYaml(String key, Object value, String path) throws Exception {Yaml yaml new Yaml(dumperOptions);String[] keys key.split(\\.);int len keys.length;//将属性转为mapFileInputStream fileInputStream new FileInputStream(new File(path));MapString, Object yamlToMap (MapString, Object)yaml.load(fileInputStream);Object oldVal getValue(key, yamlToMap);//找到key不再新增if (null ! oldVal) {return true;}MapString,Object temp yamlToMap;for (int i 0; i len - 1; i) {if (temp.containsKey(keys[i])) {temp (Map) temp.get(keys[i]);} else {temp.put(keys[i],new HashMapString,Object());temp (Map)temp.get(keys[i]);}if (i len - 2) {temp.put(keys[i 1], value);}}try {yaml.dump(yamlToMap, new FileWriter(path));} catch (Exception e) {System.out.println(yaml file insert failed !);return false;}return true;}/*** updateYaml* param paramKey a.b.c* param paramValue* param path* author senfel* date 2023/12/7 10:03* return boolean*/public static boolean updateYaml(String paramKey, Object paramValue,String path) throws Exception{Yaml yaml new Yaml(dumperOptions);//yaml文件路径String yamlUr path;Map map null;try {//将yaml文件加载为map格式map yaml.loadAs(new FileInputStream(yamlUr), Map.class);} catch (FileNotFoundException e) {e.printStackTrace();}//获取当前参数值并且修改boolean flag updateYaml(paramKey, paramValue, map, yamlUr, yaml);return flag;}/*** updateYaml* param key a.b.c* param value* param yamlToMap* param path* param yaml* author senfel* date 2023/12/7 10:51* return boolean*/public static boolean updateYaml(String key, Object value, MapString, Object yamlToMap, String path, Yaml yaml) {Object oldVal getValue(key, yamlToMap);//未找到key 不修改if (null oldVal) {return false;}try {MapString, Object resultMap setValue(yamlToMap, key, value);if (resultMap ! null) {yaml.dump(resultMap, new FileWriter(path));return true;} else {return false;}} catch (Exception e) {System.out.println(yaml file update failed !);}return false;}/*** getValue* param key a.b.c* param yamlMap* author senfel* date 2023/12/7 10:51* return java.lang.Object*/public static Object getValue(String key, MapString, Object yamlMap) {String[] keys key.split([.]);Object o yamlMap.get(keys[0]);if (key.contains(.)) {if (o instanceof Map) {return getValue(key.substring(key.indexOf(.) 1), (MapString, Object) o);} else {return null;}} else {return o;}}/*** setValue* param map* param key a.b.c* param value* author senfel* date 2023/12/7 9:59* return java.util.Mapjava.lang.String, java.lang.Object*/public static MapString, Object setValue(MapString, Object map, String key, Object value) {String[] keys key.split(\\.);int len keys.length;Map temp map;for (int i 0; i len - 1; i) {if (temp.containsKey(keys[i])) {temp (Map) temp.get(keys[i]);} else {return null;}if (i len - 2) {temp.put(keys[i 1], value);}}for (int j 0; j len - 1; j) {if (j len - 1) {map.put(keys[j], temp);}}return map;}}4、测试用例
我们分别新增、修改yaml文件进行测试。
package com.example.demo;import com.example.demo.utils.CommentUtils;
import com.example.demo.utils.YamlActionUtils;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import java.io.File;/*** YamlActionTest* author senfel* version 1.0* date 2023/12/6 17:55*/
SpringBootTest
public class YamlActionTest {Testpublic void addKey() throws Exception{String filePath D:\\workspace\\demo\\src\\main\\resources\\application.yaml;File file new File(filePath);//记录yaml文件的注释信息CommentUtils.CommentHolder holder CommentUtils.buildCommentHolder(file);//YamlActionUtils.insertYaml(spring.activemq.broker-url,http://127.0.0.1/test,filePath);//YamlActionUtils.insertYaml(spring.activemq.pool.enabled,false,filePath);YamlActionUtils.insertYaml(wx.pc.lx.enable,false,filePath);//YamlActionUtils.insertYaml(spring.activemq.in-memory,false,filePath);//YamlActionUtils.updateYaml(spring.activemq.in-memory,false,filePath);//填充注释信息holder.fillComments(file);}
}5、测试效果展示
server:port: 8888
spring:activemq:close-timeout: 15 #超时broker-url: http://127.0.0.1/test #路径pool:enabled: false # 是否开启
wx:pc:lx:enable: false如上所示 wx.pc.lx.enablefalse已经写入。 写在最后
snakeyaml编辑yaml文件并覆盖注释还是比较简单大致就是在操作yaml文件之前对注释进行缓存操作文件时先将yaml转为map然后配置数据写入并转换成yaml文件最后再将注释覆盖在yaml上即可。