yaml文件差异化比较

该文介绍了一个在多环境部署项目中,针对Nacos配置文件升级时的差异检测方法。通过使用SnakeYAML库,实现了文件夹和内容级别的比较,标记出新增、删除和更新的配置项。文章提供了Java代码示例,用于生成差异报告并保存到新目录。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

背景

多迭代、多环境部署项目时,nacos升级时不知道哪些配置文件修改了。

效果

老的配置文件:

新的配置文件:

 

文件夹级别:t2是完全一样的,删除了t5,新增了t6、t7 

 内容级别:

t1:

++rsa:
  private-key: |
    XXXXXXXXXXXXXX
  public-key: |
    QQQQQQQQQQQQQQ
++xss:
  enabled: true
  ignoreUrls:
    - /XXX/XX
    - /TTT/XX/XX
  validReferer:
    - http://localhost
    - http://192.22.81.34
---
--mail:
  smtp:
    host: XX.XXX.XX.XX
    password: 123456
    port: ''
    sender:
      address: XX
      alias: XX
    ssl:
      port: 25
    user: XX.com
---
spring:
  datasource:
    url: jdbc:mysql://XX.XXX.XX.XX:XX/${database.name:TEST}?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&allowMultiQueries=true&useSSL=false
      => jdbc:mysql://XX.XXX.XX.XX:XX/${database.name:DEV}?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&allowMultiQueries=true&useSSL=false&autoReconnect=true&failOverReadOnly=false

实现

添加maven依赖:

        <dependency>
            <groupId>org.yaml</groupId>
            <artifactId>snakeyaml</artifactId>
            <version>2.0</version>
        </dependency>

代码:

package org.example;

import lombok.Data;
import org.junit.jupiter.api.Test;
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.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * Unit test for simple App.
 */
public class AppTest {


    private String ADD_TAG = "++";
    private String DELETE_TAG = "--";
    private String UPDATE_TAG = " => ";


    private String OLD_PATH = "D:/yaml-test/old";
    private String NEW_PATH = "D:/yaml-test/new";

    private String OUT_PATH = "D:/yaml-test/update";

    @Test
    public void test() throws IOException {
        tt(OLD_PATH, NEW_PATH);
    }

    /**
     * @description 非核心逻辑
     * @author gang.tu
     */
    public void tt(String oldPath, String newPath) throws IOException {
        Yaml yaml = getYaml();
        File[] oldFile = new File(oldPath).listFiles();
        File[] newFile = new File(newPath).listFiles();
        if (!new File(OUT_PATH).exists()) {
            new File(OUT_PATH).mkdirs();
        }
        for (File nFile : newFile) {
            String updateFilePath = OUT_PATH + File.separator + nFile.getName();
            boolean find = false;
            for (File oFile : oldFile) {
                if (nFile.getName().equals(oFile.getName())) {
                    find = true;
                    List<Map<String, Object>> diffList = diff(oFile, nFile, yaml);
                    if (!diffList.isEmpty()) {
                        yaml.dumpAll(diffList.iterator(), new FileWriter(updateFilePath));
                    }
                    continue;
                }
            }
            if (!find) {
                FileChannel sourceChannel = null;
                FileChannel destChannel = null;
                try {
                    sourceChannel = new FileInputStream(nFile).getChannel();
                    destChannel = new FileOutputStream(updateFilePath + "--新增配置文件").getChannel();
                    destChannel.transferFrom(sourceChannel, 0, sourceChannel.size());
                } finally {
                    sourceChannel.close();
                    destChannel.close();
                }
            }
        }

        for (File oFile : oldFile) {
            String updateFilePath = OUT_PATH + File.separator + oFile.getName();
            boolean find = false;
            for (File nFile : newFile) {
                if (nFile.getName().equals(oFile.getName())) {
                    find = true;
                    continue;
                }
            }
            if (!find) {
                FileChannel sourceChannel = null;
                FileChannel destChannel = null;
                try {
                    sourceChannel = new FileInputStream(oFile).getChannel();
                    destChannel = new FileOutputStream(updateFilePath + "--删除配置文件").getChannel();
                    destChannel.transferFrom(sourceChannel, 0, sourceChannel.size());
                } finally {
                    sourceChannel.close();
                    destChannel.close();
                }
            }
        }
    }

    /**
     * @description 核心逻辑
     * @author gang.tu
     */
    public List<Map<String, Object>> diff(File oldFile, File newFile, Yaml yaml) throws FileNotFoundException {
        Map<String, Object> oldMap = yaml.load(new FileInputStream(oldFile));
        Map<String, Object> newMap = yaml.load(new FileInputStream(newFile));
        List<Map<String, Object>> all = new ArrayList<>();
        Map<String, Object> updateMap = new LinkedHashMap<>();
        all.add(getAdd(oldMap, newMap));
        all.add(getDelete(oldMap, newMap));
        deal(oldMap, newMap, null, updateMap);
        all.add(updateMap);
        all = all.stream().filter(e -> !e.isEmpty()).collect(Collectors.toList());
        return all;
    }


    private Yaml getYaml() {
        DumperOptions options = new DumperOptions();
        options.setIndentWithIndicator(true);
        options.setIndicatorIndent(2);
        options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
        return new Yaml(options);
    }


    private void deal(Map<String, Object> oldMap, Map<String, Object> newMap, String parentKey, Map<String, Object> resultMap) {
        Set<String> update = getUpdateKeys(oldMap.keySet(), newMap.keySet());
        Map<String, Object> updateMap;
        if (parentKey == null) {
            extracted(oldMap, newMap, update, resultMap);
        } else {
            updateMap = (Map<String, Object>) resultMap.get(parentKey);
            extracted(oldMap, newMap, update, updateMap);
        }
    }

    private void extracted(Map<String, Object> oldMap, Map<String, Object> newMap, Set<String> update, Map<String, Object> updateMap) {
        for (String key : update) {
            Object oldValue = oldMap.get(key);
            Object newValue = newMap.get(key);
            if (Objects.equals(oldValue, newValue)) {
                continue;
            }
            if (oldValue.getClass() == newValue.getClass()) {
                if (newValue instanceof Map) {
                    updateMap.put(key, new LinkedHashMap<>());
                    deal((Map<String, Object>) oldValue, (Map<String, Object>) newValue, key, updateMap);
                    if (((Map<String, Object>) updateMap.get(key)).isEmpty()) {
                        updateMap.remove(key);
                    }
                } else if (newValue instanceof List) {
                    if (!Objects.equals(oldValue, newValue)) {
                        updateMap.put(DELETE_TAG + key, oldValue);
                        updateMap.put(ADD_TAG + key, newValue);
                    }
                } else {
                    if (!Objects.equals(oldValue, newValue)) {
                        updateMap.put(key, oldValue + UPDATE_TAG + newValue);
                    }
                }
            } else {
                updateMap.put(DELETE_TAG + key, oldValue);
                updateMap.put(ADD_TAG + key, newValue);
            }
        }
    }

    private void dd() {

    }

    private Map<String, Object> getDelete(Map<String, Object> oldMap, Map<String, Object> newMap) {
        Set<String> delete = getDeleteKeys(oldMap.keySet(), newMap.keySet());
        return getResultMap(delete, oldMap, DELETE_TAG);
    }

    private Map<String, Object> getAdd(Map<String, Object> oldMap, Map<String, Object> newMap) {
        Set<String> add = getAddKeys(oldMap.keySet(), newMap.keySet());
        return getResultMap(add, newMap, ADD_TAG);
    }

    private Map<String, Object> getResultMap(Set<String> keys, Map<String, Object> map, String tag) {
        Map<String, Object> res = new LinkedHashMap<>();
        for (String key : keys) {
            res.put(tag + key, map.get(key));
        }
        return res;
    }

    private void aaa(Map<String, Object> map1, int level, String parentKey) {
        MapNode mapNode = new MapNode();
        Set<String> keys = new HashSet<>();
        Set<String> strings = map1.keySet();
        Collection<Object> values = map1.values();

        for (Map.Entry<String, Object> entry : map1.entrySet()) {
            String key = entry.getKey();
            mapNode.setLevel(level);
            keys.add(key);
            Object value = entry.getValue();
        }
    }

    @Data
    static class MapNode<K, V> {
        private int level;
        private String parentKey;
        private Set<String> keys;
        Collection<Object> values;
    }


    private Set<String> getAddKeys(Set<String> source, Set<String> target) {
        if (source == null) {
            return target;
        }
        Set<String> t = new HashSet<>(target);
        t.removeAll(source);
        return t;
    }

    private Set<String> getDeleteKeys(Set<String> source, Set<String> target) {
        if (target == null) {
            return source;
        }
        Set<String> s = new HashSet<>(source);
        s.removeAll(target);
        return s;
    }

    private Set<String> getUpdateKeys(Set<String> source, Set<String> target) {
        if (source == null) {
            return target;
        }
        Set<String> t = new HashSet<>(target);
        t.retainAll(source);
        return t;
    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值