使用Java将properties转为yaml,保证顺序、实测无BUG版本

使用Java将properties转为yaml

    • 一 前言
      • 1.1 顺序错乱的原因
      • 1.2 遗漏子节点的原因
    • 二、优化措施
    • 三、源码

一 前言

浏览了一圈网上的版本,大多存在以下问题:

  • 转换后顺序错乱
  • 遗漏子节点

基于此进行了优化,如果只是想直接转换,可直接使用我发布的 在线版本
在这里插入图片描述
如果想在工程中使用,可继续往下看,源码在文末。

1.1 顺序错乱的原因

大部分代码都是使用了java.util.Properties类来转换,这个类是基于ConcurrentHashMap来存储键值对的,必然会顺序错乱

这是截取的Properties类的部分源码:

/**
  * Properties does not store values in its inherited Hashtable, but instead
  * in an internal ConcurrentHashMap.  Synchronization is omitted from
  * simple read operations.  Writes and bulk operations remain synchronized,
  * as in Hashtable.
  */
private transient volatile ConcurrentHashMap<Object, Object> map;

/**
     * Creates an empty property list with the specified defaults.
     *
     * @implNote The initial capacity of a {@code Properties} object created
     * with this constructor is unspecified.
     *
     * @param   defaults   the defaults.
     */
public Properties(Properties defaults) {
    this(defaults, 8);
}

private Properties(Properties defaults, int initialCapacity) {
    // use package-private constructor to
    // initialize unused fields with dummy values
    super((Void) null);
    map = new ConcurrentHashMap<>(initialCapacity);
    this.defaults = defaults;

    // Ensure writes can't be reordered
    UNSAFE.storeFence();
}

1.2 遗漏子节点的原因

主要还是代码不够严谨,解析的时候没有判断子节点是否是数组或对象,粗暴的转为String直接赋值了

二、优化措施

  • 基于LinkedHashMap来存储键值对

  • 递归遍历时判断子节点类型,不同类型采用不同的处理方式

三、源码

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author Deng.Weiping
 * @since 2023/11/28 13:57
 */
@Slf4j
public class PropertiesParser {

    /**
     * Properties 转 Yaml
     *
     * @param input
     * @return
     */
    public static String castToYaml(String input) {
        try {
            Map<String, Object> properties = readProperties(input);
            return properties2Yaml(properties);
        } catch (Exception e) {
            log.error("property 转 Yaml 转换失败", e);
        }
        return null;
    }

    private static Map<String, Object> readProperties(String input) {
        // 使用 LinkedHashMap 保证顺序
        Map<String, Object> propertiesMap = new LinkedHashMap<>();
        for (String line : input.split(StrUtil.LF)) {
            if (StrUtil.isNotBlank(line)) {
                // 使用正则表达式解析每一行中的键值对
                Pattern pattern = Pattern.compile("\\s*([^=\\s]*)\\s*=\\s*(.*)\\s*");
                Matcher matcher = pattern.matcher(line);
                if (matcher.matches()) {
                    String key = matcher.group(1);
                    String value = matcher.group(2);
                    propertiesMap.put(key, value);
                }
            }
        }
        return propertiesMap;
    }

    /**
     * properties 格式转化为 yaml 格式字符串
     *
     * @param properties
     * @return
     */
    private static String properties2Yaml(Map<String, Object> properties) {
        if (CollUtil.isEmpty(properties)) {
            return null;
        }
        Map<String, Object> map = parseToMap(properties);
        StringBuffer stringBuffer = map2Yaml(map);
        return stringBuffer.toString();
    }

    /**
     * 递归解析为 LinkedHashMap
     *
     * @param propMap
     * @return
     */
    private static Map<String, Object> parseToMap(Map<String, Object> propMap) {
        Map<String, Object> resultMap = new LinkedHashMap<>();
        try {
            if (CollectionUtils.isEmpty(propMap)) {
                return resultMap;
            }
            propMap.forEach((key, value) -> {
                if (key.contains(".")) {
                    String currentKey = key.substring(0, key.indexOf("."));
                    if (resultMap.get(currentKey) != null) {
                        return;
                    }
                    Map<String, Object> childMap = getChildMap(propMap, currentKey);
                    Map<String, Object> map = parseToMap(childMap);
                    resultMap.put(currentKey, map);
                } else {
                    resultMap.put(key, value);
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
        return resultMap;
    }


    /**
     * 获取拥有相同父级节点的子节点
     *
     * @param propMap
     * @param currentKey
     * @return
     */
    private static Map<String, Object> getChildMap(Map<String, Object> propMap, String currentKey) {
        Map<String, Object> childMap = new LinkedHashMap<>();
        try {
            propMap.forEach((key, value) -> {
                if (key.contains(currentKey + ".")) {
                    key = key.substring(key.indexOf(".") + 1);
                    childMap.put(key, value);
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
        return childMap;
    }

    /**
     * map集合转化为yaml格式字符串
     *
     * @param map
     * @return
     */
    public static StringBuffer map2Yaml(Map<String, Object> map) {
        //默认deep 为零,表示不空格,deep 每加一层,缩进两个空格
        return map2Yaml(map, 0);
    }

    /**
     * 把Map集合转化为yaml格式 String字符串
     *
     * @param propMap map格式配置文件
     * @param deep    树的层级,默认deep 为零,表示不空格,deep 每加一层,缩进两个空格
     * @return
     */
    private static StringBuffer map2Yaml(Map<String, Object> propMap, int deep) {
        StringBuffer yamlBuffer = new StringBuffer();
        try {
            if (CollectionUtils.isEmpty(propMap)) {
                return yamlBuffer;
            }
            String space = getSpace(deep);
            for (Map.Entry<String, Object> entry : propMap.entrySet()) {
                Object valObj = entry.getValue();
                if (entry.getKey().contains("[") && entry.getKey().contains("]")) {
                    String key = entry.getKey().substring(0, entry.getKey().indexOf("[")) + ":";
                    yamlBuffer.append(space + key + "\n");
                    propMap.forEach((itemKey, itemValue) -> {
                        if (itemKey.startsWith(key.substring(0, entry.getKey().indexOf("[")))) {
                            yamlBuffer.append(getSpace(deep + 1) + "- ");
                            if (itemValue instanceof Map) {
                                StringBuffer valStr = map2Yaml((Map<String, Object>) itemValue, 0);
                                String[] split = valStr.toString().split(StrUtil.LF);
                                for (int i = 0; i < split.length; i++) {
                                    if (i > 0) {
                                        yamlBuffer.append(getSpace(deep + 2));
                                    }
                                    yamlBuffer.append(split[i]).append(StrUtil.LF);
                                }
                            } else {
                                yamlBuffer.append(itemValue + "\n");
                            }
                        }
                    });
                    break;
                } else {
                    String key = space + entry.getKey() + ":";
                    if (valObj instanceof String) { //值为value 类型,不用再继续遍历
                        yamlBuffer.append(key + " " + valObj + "\n");
                    } else if (valObj instanceof List) { //yaml List 集合格式
                        yamlBuffer.append(key + "\n");
                        List<String> list = (List<String>) entry.getValue();
                        String lSpace = getSpace(deep + 1);
                        for (String str : list) {
                            yamlBuffer.append(lSpace + "- " + str + "\n");
                        }
                    } else if (valObj instanceof Map) { //继续递归遍历
                        Map<String, Object> valMap = (Map<String, Object>) valObj;
                        yamlBuffer.append(key + "\n");
                        StringBuffer valStr = map2Yaml(valMap, deep + 1);
                        yamlBuffer.append(valStr.toString());
                    } else {
                        yamlBuffer.append(key + " " + valObj + "\n");
                    }
                }

            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return yamlBuffer;
    }

    /**
     * 获取缩进空格
     *
     * @param deep
     * @return
     */
    private static String getSpace(int deep) {
        StringBuffer buffer = new StringBuffer();
        if (deep == 0) {
            return "";
        }
        for (int i = 0; i < deep; i++) {
            buffer.append("  ");
        }
        return buffer.toString();
    }

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/203329.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

pandas(八)--实战一下

背景 收到一批数据&#xff0c;数据形式。采集数据的间隔时间是10分钟&#xff0c;全天采集数据&#xff0c;每天的数据量是144条 处理后的数据形式 分析 去除表格中的q的异常值&#xff0c;置为0去除重复行将原始表格中的date分裂成日期和时间缺失的时间点数据补0&#x…

Target、沃尔玛撸卡、采退支付下单如何避免账号关联风险?

近年来&#xff0c;随着跨境电商平台的日益繁荣&#xff0c;越来越多的国内卖家涌入其中&#xff0c;导致竞争异常激烈。为了在竞争中脱颖而出&#xff0c;一些卖家采用自动脚本程序进行浏览和下单&#xff0c;然而这种行为很容易导致账号被批量关联、封号。本文将探讨养号下单…

JOSEF 时间继电器 DS-37 AC220V 10秒 柜内安装,板前接线

系列型号 DS-31时间继电器&#xff1b;DS-31/X时间继电器&#xff1b; DS-31/2X时间继电器&#xff1b;DS-32时间继电器&#xff1b; DS-32/X时间继电器;DS-32/2X时间继电器; DS-33时间继电器;DS-33/X时间继电器; DS-33/2X时间继电器;DS-34时间继电器; DS-31C/X时间继电器…

聊一聊大模型 | 京东云技术团队

事情还得从ChatGPT说起。 2022年12月OpenAI发布了自然语言生成模型ChatGPT&#xff0c;一个可以基于用户输入文本自动生成回答的人工智能体。它有着赶超人类的自然对话程度以及逆天的学识。一时间引爆了整个人工智能界&#xff0c;各大巨头也纷纷跟进发布了自家的大模型&#…

MySQL进阶知识:三

前言 未更新完毕&#xff01;大概明天更完&#xff01; 锁 MySQL中的锁&#xff0c;按照锁的粒度分&#xff0c;分为以下三类 全局锁&#xff1a;锁定数据库中的所有表。表级锁&#xff1a;每次操作锁住整张表。行级锁&#xff1a;每次操作锁住对应的行数据。 全局锁 全局…

14.docker部署应用的两种企业实践

1.介绍 1.1 说明 这里仅仅说明一下在企业应用的两种简单实践&#xff0c;并不包含自动化这套东西。这里说的两种实践&#xff0c;是我在工作中不同公司使用的两种部署方式&#xff0c;仅供大家参考。 1.2 两种部署方式 第一种&#xff1a;是把环境和app都一起打成一个镜像&…

TCP 连接断开

1&#xff1a;TCP 四次挥手过程是怎样的&#xff1f; 客户端打算关闭连接&#xff0c;此时会发送一个 TCP 首部 FIN 标志位被置为 1 的报文&#xff0c;也即 FIN 报文&#xff0c;之后客户端进入 FIN_WAIT_1 状态。 服务端收到该报文后&#xff0c;就向客户端发送 ACK 应答报文…

【开源】基于Vue.js的超市账单管理系统的设计和实现

项目编号&#xff1a; S 032 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S032&#xff0c;文末获取源码。} 项目编号&#xff1a;S032&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块三、系统设计3.1 总体设计3.2 前端设计3…

mybatis数据输入-实体类型的参数

1、建库建表 CREATE DATABASE mybatis-example;USE mybatis-example;CREATE TABLE t_emp(emp_id INT AUTO_INCREMENT,emp_name CHAR(100),emp_salary DOUBLE(10,5),PRIMARY KEY(emp_id) );INSERT INTO t_emp(emp_name,emp_salary) VALUES("tom",200.33); INSERT INTO…

Python文件操作,看这篇就足够!

Python中有几个内置模块和方法来处理文件。这些方法被分割到例如os, os.path , shutil 和 pathlib 等等几个模块中。文章将列举Python中对文件最常用的操作和方法。 在这篇文章中&#xff0c;你将学习如何&#xff1a; 获取文件属性创建目录文件名模式匹配遍历目录树创建临时…

python实现获取aws route53域名信息

最近由于工作原因接触到aws的服务&#xff0c;我需要实时获取所有的域名信息&#xff0c;用于对其进行扫描&#xff0c;因此写了一个自动化爬取脚本 给需要的人分享。 1.基础准备 代码环境&#xff1a;python3 第三方库&#xff1a;boto3 &#xff08;安装方法pip install…

【web安全】CSRF漏洞攻击与防御

前言 总结&#xff0c;仅供学习。 csrf的理解 我们了解一个网站有修改信息&#xff0c;密码&#xff0c;添加删除管理&#xff0c;支付转账的功能之后。 通过抓包抓取对方修改操作的数据包样式&#xff0c; 然后在自己网站搭建一个指令。 当别人来访时&#xff0c; 如果…

接口01-Java

接口-Java 一、引入(快速入门案例)二、接口介绍1、概念2、语法 三、应用场景四、接口使用注意事项五、练习题1 一、引入(快速入门案例) usb插槽就是现实中的接口。 你可以把手机、相机、u盘都插在usb插槽上&#xff0c;而不用担心那个插槽是专门插哪个的&#xff0c;原因是做u…

行业唯一!法大大上榜2023德勤深圳明日之星

11月28日&#xff0c;德勤中国在深圳举办2023德勤深圳高科技高成长20强及明日之星&#xff08;以下简称“深圳明日之星”&#xff09;颁奖盛典。法大大作为高速成长、持续创新的卓越企业荣登深圳明日之星榜单。值得一提的是&#xff0c;法大大是唯一一家获奖的电子签企业&#…

PostgreSQL | EXTRACT | 获取时间的年月日字串

EXTRACT EXTRACT 函数是 PostgreSQL 中用于从日期和时间类型中提取特定部分&#xff08;如年、月、日、小时等&#xff09;的函数。 格式 EXTRACT(field FROM source) -- field 参数是要提取的部分&#xff0c;例如 YEAR、MONTH、DAY、HOUR 等。 -- source 参数是包含日期或…

傅里叶变换及其在机器学习中的应用

​​​​​​​一、介绍 傅立叶变换是一种数学技术&#xff0c;在各个科学和工程领域发挥着关键作用&#xff0c;其应用范围从信号处理到量子力学。近年来&#xff0c;它在机器学习领域发现了新的意义。本文探讨了傅里叶变换的基础知识及其在机器学习应用中日益增长的重要性。 …

uniapp上架app store详细攻略

目录 uniapp上架app store详细攻略 前言 一、登录苹果开发者网站 二、创建好APP 前言 uniapp开发多端应用&#xff0c;打包ios应用后&#xff0c;会生成一个ipa后缀的文件。这个文件无法直接安装在iphone上&#xff0c;需要将这个ipa文件上架app store后&#xff0c;才能通…

【经验分享】openGauss 客户端(Data Studio / DBeaver)连接方式

前言 本篇介绍了openGauss常用的客户端连接工具Data Studio和DBeaver 01 客户端工具 openGauss部署之后&#xff0c;在服务器上提供了在命令行下运行的数据库连接工具gsql。此工具除了具备操作数据库的基本功能&#xff0c;还提供了若干高级特性&#xff0c;便于用户使用。…

插槽slot使用

场景&#xff1a;el-button是绝对定位&#xff0c;希望它能根据query组件&#xff08;公共组件&#xff09;定位&#xff0c;query组件是相对定位。 <query :queryArr"queryParams" class"query"><div class"btn"><el-button cla…

王者小游戏

游戏里的经验动物 Bear package beast; import sxt.GameFrame; public class Bear extends Beast {public Bear(int x, int y, GameFrame gameFrame) {super(x, y, gameFrame);setImg("C:\\Users\\辛欣\\OneDrive\\桌面\\王者荣耀图片(1)\\王者荣耀图片\\beast\\bear.jp…