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工具,然后将工具转换结果 与 该方法结果比较即可。

 

posted @ 2020-08-27 11:32  <予安>  阅读(6877)  评论(1编辑  收藏  举报