java 读写yml文件,修改文件内容保持原格式
我理解的yml文件
在我们学习过springboot项目的同学对这个文件肯定是特别熟悉,通常我们都是用它来修改保存我们的项目配置,最简单的就是数据库的配置,然后就是资源读取配置,redies配置,缓存配置,以及jwt的配置等等,因为yml文件主要用于我们的项目配置,所以一个特点就是易于人们阅读,修改;即使一个刚接触yml文件的人加上简单的注释,肯定也能够看懂打部分内容的;
我总结的特点就是:
- 直观,易于阅读,修改
- 可以把对象的层次结构展现得很直观
- 用key:value的方式属性
- 用空格数量表示层级关系
我工作中遇到的问题
大家估计在spring中主要都是读取yml文件,所以对于写yml文件的过程经历不太多,我的项目系统是自己的java框架,然后要集成另外的一个spring项目,当我的项目修改数据库信息的时候同时也要修改这个springboot项目的数据库配置信息,那么就存在一个对yml的读和写的过程,我经过百度后加上自己的代码,就得到了自己想要的效果;主要用到了ymal这个开源工具类;
maven依赖
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.25</version>
</dependency>
读取
关键代码,ymal工具自动将yml文件转换为map结构的对象
//解析yml文件的关键类
Yaml yml =null;
try (FileInputStream in = new FileInputStream(file)){
yml = new Yaml();
obj = (Map)yml.load(in);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
Map<String,Object> springMap =(Map)obj.get("spring");
写入
关键代码,
try (FileWriter writer = new FileWriter(file)) {
//writer.write(yml.dump(obj));
writer.write(yml.dumpAsMap(obj));
//writer.write(yml.dumpAs(obj, Tag.MAP, DumperOptions.FlowStyle.BLOCK));
//可以自定义写入格式
//writer.write(yml.dumpAs(obj, Tag.MAP, DumperOptions.FlowStyle.FLOW));
} catch (IOException e) {
e.printStackTrace();
}
我用的是dumpAsMap方法,这个方法能够自动把map结构的对象转为yml文件的key:value的结构,map层级用两个空格表示,但是我的这个项目用了4个空格,所以后面我还得重新特殊处理一下;
完整代码,
因为我的主要目的是修改数据库配置信息,由于项目系统支持了很多类型的数据库,所以我要适配各种数据库;
@SpringBootTest
public class DemoTest {
@Test
public void readYmlTest() {
//要读的文件路径
String filePath ="C:"+File.separator+"Users"+File.separator+"17247"+File.separator+"Desktop"+File.separator+"application-exclusive.yml";
Map<String,Object> obj =null;
File file = new File(filePath);
//解析yml文件的关键类
Yaml yml =null;
try (FileInputStream in = new FileInputStream(file)){
yml = new Yaml();
obj = (Map)yml.load(in);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
Map<String,Object> springMap =(Map)obj.get("spring");
String url = "jdbc:oracle:thin:@127.0.0.1:1521/orcl";
//String url = "jdbc:dm://127.0.0.1:5236/DMSERVER?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=utf-8";
String usesrname = "poc";
String pwd = "ENC(w2PXuzg+U60Zs2MA/FouyQ==)";
AEDatasourceFactory AeDatasourceFactory = new AEDatasourceFactory(url,usesrname,pwd);
//根据不同数据库更新模板
AeDatasourceFactory.updateDatasourceTemplet(springMap);
try (FileWriter writer = new FileWriter(file)) {
//writer.write(yml.dump(obj));
//writer.write(yml.dumpAsMap(obj));
writer.write(yml.dumpAs(obj, Tag.MAP, DumperOptions.FlowStyle.BLOCK));
//writer.write(yml.dumpAs(obj, Tag.MAP, DumperOptions.FlowStyle.BLOCK));
} catch (IOException e) {
e.printStackTrace();
}
//重新读取文件调整空格
StringBuffer buffer = new StringBuffer();
String newLine =System.lineSeparator();
try (BufferedReader input = new BufferedReader(
new InputStreamReader(new FileInputStream(file), "utf-8"));) {
String line;
while ((line = input.readLine()) != null) {
String lineTrim = line.trim();
String pre ="";
if(line.startsWith(" ")){
int length = line.length()-lineTrim.length();
pre = this.getSpaceByNum(length*2);
}
if(lineTrim.startsWith("- ")){
//有横杠的需要再加4个空格
pre = pre+this.getSpaceByNum(4);
}
buffer.append(pre+lineTrim);
buffer.append(newLine);
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
try (BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(new FileOutputStream(filePath, false), "utf-8"))) {
writer.write(buffer.toString());
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
//获取指定数量的空格
private String getSpaceByNum(int num){
StringBuffer buffer = new StringBuffer();
for(int i=0;i<num;i++){
buffer.append(" ");
}
return buffer.toString();
}
private String getAEEncryptPassword(String input) {
return input ;
}
}
适配数据库工厂类
import java.util.LinkedHashMap;
import java.util.Map;
/**
* AE数据源模板工厂类
*/
public class AEDatasourceFactory {
//oracle数据库
private final String ORACLE_TYPE="jdbc:oracle";
//sqlserver数据库类型
private final String SQLSERVER_TYPE="jdbc:oracle";
//db2数据库
private final String DB2_TYPE="jdbc:db2";
//达梦数据库
private final String DM_TYPE="jdbc:dm";
//pg数据库
private final String PG_TYPE="dbc:postgresql";
//高斯数据库
private final String GAUSS_TYPE="jdbc:zenith";
String url;
String username;
String password;
public AEDatasourceFactory(String url, String username, String password) {
this.url=url;
this.username=username;
this.password=password;
}
/**
* 获取更新数据源模板
* @param springMap
*/
public void updateDatasourceTemplet(Map<String,Object> springMap){
Map<String,Object> dataSource = (Map<String,Object>)springMap.get("datasource");
Map<String,Object> jpaMap = (Map<String,Object>)dataSource.get("jpa");
if(jpaMap==null){
//应该是jpa和datasource同级的,
// 但是他们提供的可能是不同级的
jpaMap = springMap.get("jpa")!=null?(Map<String,Object>)springMap.get("jpa"):new LinkedHashMap<>();
}
dataSource.clear();
Map<String,Object> hikari = new LinkedHashMap<>();
hikari.put("auto-commit",false);
if(url.startsWith(ORACLE_TYPE) || url.startsWith(SQLSERVER_TYPE)){
dataSource.put("type","com.zaxxer.hikari.HikariDataSource");
dataSource.put("url",url);
dataSource.put("username",username);
dataSource.put("password",getAEEncryptPassword(password));
dataSource.put("hikari",hikari);
}else if(url.startsWith(DM_TYPE)){
dataSource.put("url",url);
dataSource.put("username",username);
dataSource.put("password",getAEEncryptPassword(password));
dataSource.put("driver-class-name","dm.jdbc.driver.DmDriver");
}else if(url.startsWith(DB2_TYPE)){
dataSource.put("type","com.zaxxer.hikari.HikariDataSource");
dataSource.put("driver-class-name","com.ibm.db2.jcc.DB2Driver");
dataSource.put("hikari",hikari);
}else if(url.startsWith(GAUSS_TYPE)){
dataSource.put("url",url);
dataSource.put("username",username);
dataSource.put("password",getAEEncryptPassword(password));
dataSource.put("driver-class-name","com.huawei.gauss.jdbc.ZenithDriver");
}else if(url.startsWith(PG_TYPE)){
dataSource.put("type","com.zaxxer.hikari.HikariDataSource");
dataSource.put("url",url);
dataSource.put("username",username);
dataSource.put("password",getAEEncryptPassword(password));
dataSource.put("hikari",hikari);
//pg没有jpa
jpaMap=null;
}
if(jpaMap!=null){
getJpaTemplet(jpaMap);
springMap.put("jpa",jpaMap);
}
}
private void getJpaTemplet(Map<String,Object> jpaMap){
if(jpaMap==null){
return;
}
jpaMap.clear();
Map<String,Object> properties = new LinkedHashMap<>();
if(url.startsWith(ORACLE_TYPE) || url.startsWith(SQLSERVER_TYPE)){
jpaMap.put("database-platform","org.hibernate.dialect.Oracle12cDialect");
jpaMap.put("database","ORACLE");
jpaMap.put("show-sql",false);
properties.put("hibernate.id.new_generator_mappings",true);
properties.put("hibernate.connection.provider_disables_autocommit",true);
properties.put("hibernate.cache.use_second_level_cache",false);
properties.put("hibernate.cache.use_query_cache",false);
properties.put("hibernate.generate_statistics",false);
jpaMap.put("properties",properties);
}else if(url.startsWith(GAUSS_TYPE)){
//不知道为啥要用oracle的
properties.put("hibernate.dialect","org.hibernate.dialect.Oracle12cDialect");
jpaMap.put("properties",properties);
}else if(url.startsWith(DM_TYPE)){
properties.put("hibernate.dialect","org.hibernate.dialect.DmDialect");
jpaMap.put("properties",properties);
}else if(url.startsWith(PG_TYPE)){
}else if(url.startsWith(DB2_TYPE)){
properties.put("hibernate.dialect","com.diwork.intelliv.workbench.auth.dialect.CustomDB2Dialect");
properties.put("hibernate.auto_quote_keyword",true);
jpaMap.put("properties",properties);
jpaMap.put("show-sql",true);
Map<String,Object> hibernate = new LinkedHashMap<>();
Map<String,Object> naming = new LinkedHashMap<>();
naming.put("physical-strategy","org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl");
hibernate.put("naming",naming);
jpaMap.put("hibernate",hibernate);
}
}
//密码加密方法
private String getAEEncryptPassword(String input) {
return input ;
}
}
这样修改了指定的内容,而且还保证了文件的原格式,但有个问题是文件的注释会丢失;
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 全网最简单!3分钟用满血DeepSeek R1开发一款AI智能客服,零代码轻松接入微信、公众号、小程
· .NET 10 首个预览版发布,跨平台开发与性能全面提升
· 《HelloGitHub》第 107 期
· 全程使用 AI 从 0 到 1 写了个小工具
· 从文本到图像:SSE 如何助力 AI 内容实时呈现?(Typescript篇)