zoukankan      html  css  js  c++  java
  • yaml文件 转为 properties —使用java实现

    若要转载本文,请务必声明出处:https://www.cnblogs.com/zhongyuanzhao000/p/13570432.html


    最近要用Java实现一个 将yaml文件转为properties形式的功能,作为一个老搬运工,肯定先查一查有没有已经开源的代码啦,[狗头]!

    发现其实有许多在线转换工具,比如:https://www.toyaml.com/,这是一个在线yaml与properties互转的工具,效果很不错,如果只是偶尔进行文件互转,可以考虑用这个,优点是简单便捷。

    然而,我需要一段Java代码来实现 yaml转 properties的功能。。。

    发现真有优秀的大佬分享了 yaml转properties 的java代码,感谢大佬的分享,详见:https://www.cnblogs.com/xujingyang/p/10613206.html。简单地测试了这份代码,优点是可以实现基本的转换功能,但是我的需求比较多,还不能满足要求。所以,基于这份代码,我进行了功能完善,支持更多的转换逻辑。

    好了,废话不多说,下面开搞吧!


    注意,由于借鉴了大佬的源码,所以下面的代码大体结构都是相似的。

    步骤如下:

    1、引入yaml转properties所需的maven依赖

    <dependency>
        <groupId>com.fasterxml.jackson.dataformat</groupId>
        <artifactId>jackson-dataformat-yaml</artifactId>
        <version>2.9.4</version>
    </dependency>

    2、工具类如下:

    package com.zzy.utils;
    
    import com.fasterxml.jackson.core.JsonToken;
    import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
    import com.fasterxml.jackson.dataformat.yaml.YAMLParser;
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.InputStreamReader;
    import java.nio.charset.Charset;
    import java.util.LinkedList;
    import java.util.List;
    
    /**
     * @author zhongyuanzhao000
     * @date 2020/08/27
     */
    public class FileTranferUtils {
    
        /**
         * tranfer yaml file to properties
         * @param path the path of yaml file
         */
        public static void yaml2Prop(String path) {
            List<String> lines = new LinkedList<>();
    
            // DOT用于隔开key中不同的键
            final String DOT = "*";
    
            //layerOfArray表示当前数组是第几层数组,默认为0即没有数组;每遍历到"["就增加1
            int layerOfArray = 0;
    
            // inArrays的索引表示yml文件中遍历到的token位于第几层数组,而元素内容表示当前遍历到的token仍在数组内部,元素默认值为false
            boolean[] inArrays = new boolean[4];
            // arrayIndexs的索引表示yml文件中遍历到的token位于第几层数组,而元素内容表示其对应的索引,元素默认值为0
            int[] arrayIndexs = new int[4];
            // arrayCuteds的索引表示yml文件中遍历到的token位于第几层数组,而元素内容表示 含有中括号的键是否已被切去,元素默认值为false
            boolean[] arrayCuteds = new boolean[4];
            // 注意:上面3个数组,目前均初始化了4个元素值,对应0、1、2、3,表示可以解析最多3层数组嵌套;
            // 若要更多层,修改该初始值即可
    
            try {
                YAMLFactory yamlFactory = new YAMLFactory();
                YAMLParser parser = yamlFactory.createParser(new InputStreamReader(new FileInputStream(path), Charset.forName("utf-8")));
    
                String key = ""; //这里的key是最终转换出的properties文件中key,并不是yml文件中的键值对中的键
                String value = null;
                // 先获取到基于json格式的第一个token,便于后面的遍历
                JsonToken token = parser.nextToken();
                while (token != null) {
    
                    // 基于json格式,如果是一个对象开始(即遇到了左花括号"{"时)
                    if (JsonToken.START_OBJECT.equals(token)) {
                        // do nothing
                    } else if (JsonToken.FIELD_NAME.equals(token)) {   // 基于json格式,如果遇到键值对的键时
    
                        // 使用点"."分割每层的key
                        if (key.length() > 0) {
                            // 如果该对象在数组中,并且key包含中括号的数量不等于 当前所在数组的层次时,则添加上数组索引
                            if (inArrays[layerOfArray] == true && containNumbers(key, "[") != layerOfArray) {
                                key = key + "[" + arrayIndexs[layerOfArray] + "]";
                            }
                            key = key + DOT;
                        }
                        key = key + parser.getCurrentName();
    
                        // 继续遍历下一个token
                        token = parser.nextToken();
                        /******************************************************************************************/
                        //如果遇到左中括号"[",表示数组的开始
                        if (JsonToken.START_ARRAY.equals(token)) {
                            // 进入第一层数组
                            layerOfArray++;
                            inArrays[layerOfArray] = true;
    
                            token = parser.nextToken();
                        }
                        /******************************************************************************************/
    
                        // 如果遇到子对象的开始(即"{"),则跳入下一个循环
                        if (JsonToken.START_OBJECT.equals(token)) {
                            continue;
                        }
    
                        /******************************************************************************************/
                        // 此时,当前token遍历到了一个键值对的值时(即到了一个分支尽头),需要将其放入string数组中
                        value = parser.getText();
                        //如果这个值是单独被包含在中括号数组内(中括号内没有键值对 对应的键),则key肯定还没有在相应的键上添加索引,所以这里要补上索引
                        if (inArrays[layerOfArray] == true && containNumbers(key, "[") != layerOfArray) {
                            key = key + "[" + arrayIndexs[layerOfArray] + "]";
                        }
                        lines.add(key + "=" + value);
    
                        /******************************************************************************************/
                        // 每当遍历完一个分支,需要将 key截断到倒数第二个键
                        int dotOffset = key.lastIndexOf(DOT);
                        if (key.length() - 1 == key.lastIndexOf("]")) {
                            arrayCuteds[layerOfArray] = true;
                        }
                        if (dotOffset > 0) {
                            key = key.substring(0, dotOffset);
                        } else {
                            // 若原key中没有".",则key直接置为""
                            key = "";
                        }
    
    
                        // 若截断后剩下的key的最后一个键含有中括号,也就是该键的索引即将更新,则去除掉该中括号子串以便于后面添加新的索引
                        if (key.length() > 0 && key.length() - 1 == key.lastIndexOf("]")) {
                            key = key.substring(0, key.lastIndexOf("["));
                        }
    
                    }else if (JsonToken.END_OBJECT.equals(token)) {    // 基于json格式,如果是一个对象结束(即遇到了右花括号"}"时)
    
                        // 如果当前token在数组内部,则不需要截断
                        if (inArrays[layerOfArray]) {
                            arrayIndexs[layerOfArray]++;
                        } else {
                            int dotOffset = key.lastIndexOf(DOT);
                            if (dotOffset > 0) {
                                // 若原key中还有".",则截断到倒数第二个键
                                key = key.substring(0, dotOffset);
                            } else {
                                // 若原key中没有".",则key直接置为""
                                key = "";
                            }
                        }
    
                    } else if (JsonToken.END_ARRAY.equals(token)) {  //如果遇到右中括号"]",表示数组的结束
                        // 若当前层次中 含有中括号的键未被切去
                        if (!arrayCuteds[layerOfArray]) {
                            int dotOffset = key.lastIndexOf(DOT);
                            if (dotOffset > 0) {
                                // 若原key中还有".",则截断到倒数第二个键
                                key = key.substring(0, dotOffset);
                            } else {
                                // 若原key中没有".",则key直接置为""
                                key = "";
                            }
                        }
    
                        // 重置该层的变量
                        inArrays[layerOfArray] = false;
                        arrayIndexs[layerOfArray] = 0;
                        arrayCuteds[layerOfArray] = false;
    
                        // 回退到上一层
                        layerOfArray--;
    
                        // 若截断后剩下的key的最后一个键含有中括号,也就是上一层中 该键的索引即将更新,则去除掉该中括号子串 以便于后面添加新的索引
                        if (key.length() > 0 && key.length() - 1 == key.lastIndexOf("]")) {
                            key = key.substring(0, key.lastIndexOf("["));
                        }
                    }
                    token = parser.nextToken();
                }
                parser.close();
    
                // 将String字符串数组中的内容打印出来
                for (String line : lines) {
                    line = line.replace(DOT, ".");
                    System.out.println(line);
                }
    
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    
        /**
         * 计算 字符串str中包含多少个 模式串s
         * @param str 主字符串
         * @param s 模式串
         * @return
         */
        private static int containNumbers(String str, String s) {
            int count = 0;
            for(int i = 0; i < str.length(); ){
                int c = -1;
                c = str.indexOf(s);
                if (c != -1){
                    str = str.substring(c + 1);
                    count ++;
                } else {
                    break;
                }
            }
            return count;
        }
    
    }

    使用yaml2Prop方法,指定yml文件路径就可以输出properties内容在控制台上了。

    如果需要了解该方法的代码,建议先参考jackson仓库,然后将yml文件内容转为json形式,再根据json形式来理解方法代码。

    最后,如果要检验该方法的效果,可以使用在线yaml转properties工具,然后将工具转换结果 与 该方法结果比较即可。

  • 相关阅读:
    [LeetCode] 909. Snakes and Ladders 蛇梯棋
    [LeetCode] 857. Minimum Cost to Hire K Workers 雇佣K名工人的最低成本
    [LeetCode] 908. Smallest Range I 最小区间
    [LeetCode] 862. Shortest Subarray with Sum at Least K 和至少为K的最短子数组
    [LeetCode] 907. Sum of Subarray Minimums 子数组最小值之和
    [LeetCode] 864. Shortest Path to Get All Keys 获得所有钥匙的最短路径
    [LeetCode] 906. Super Palindromes 超级回文数
    [LeetCode] 871. Minimum Number of Refueling Stops 最少的加油站个数
    [LeetCode] 905. Sort Array By Parity 按奇偶排序数组
    [LeetCode] 1028. Recover a Tree From Preorder Traversal 从先序遍历还原二叉树
  • 原文地址:https://www.cnblogs.com/zhongyuanzhao000/p/13570432.html
Copyright © 2011-2022 走看看