C# 自定义配置文件序列化生成+文件格式错误自动回档

文章目录

  • 前言
  • 选择Xml
  • 简单的Xml使用
    • 测试用例
    • 简单的写
    • 简单的读
    • 简单的生成配置
      • 修改配置类
      • 测试用例
      • 运行结果对比
  • 代码逻辑封装
    • 逻辑示意
    • 封装好的代码
    • 测试生成
    • 配置文件格式错误测试
      • 使用默认值覆盖来解决问题
  • 配置文件人为修改错误如何解决
    • 解决方案
    • 代码
    • 测试用例
    • 运行结果
  • 代码封装总结
  • 总结

前言

一般我们代码生成了之后,就不会动了。而可动的参数一般写在配置文件里面。配置语言的格式一般有一下几种

优点缺点
xml扩展性强,歧义性小对于人来说过于冗长
Json可读性强无法添加注释
yaml可读取强缩进地狱,手动修改时极其容易出现问题

选择Xml

首先Xml的文件不是我们自己生成的,而是机器自己主动生成的。因为我们一般的使用逻辑是

程序生成默认配置文件
人为修改文件
程序读取修改后的结果

对于我们配置人员来说,修改的部分是比较少的,而且由于其极强的拓展性,可以添加许多的注释。所以我打算使用Xml来生成对应的配置文档。而Json由于其修改时容易出错和扩展性的问题,我暂时就不用了。

简单的Xml使用

微软其实已经帮我们封装好了Xml的操控类。这里直接用序列化对象就行了

微软 XML 序列化示例。

测试用例

  public class MyConfigService{public string Name { get; set; }public string Description { get; set; }public int Id { get; set; }public bool IsEnabled { get; set; }public enum MyKeys { Apple,Banana,Pear}public MyKeys SettingKey { get; set; }public MyConfigService() { }}

简单的写

            MyConfigService myConfigService = new MyConfigService() {Name = "坤坤",Description = "偶像练习生",Id = 114514,IsEnabled = false,SettingKey = MyConfigService.MyKeys.Pear};var xmlHelper = new XmlSerializer(typeof(MyConfigService));StreamWriter xmlWriter = new StreamWriter("MyConfig.xml");xmlHelper.Serialize(xmlWriter, myConfigService);xmlWriter.Close();

在这里插入图片描述

简单的读

     static void Main(string[] args){MyConfigService myConfigService = new MyConfigService(){Name = "坤坤",Description = "偶像练习生",Id = 114514,IsEnabled = false,SettingKey = MyConfigService.MyKeys.Pear};var xmlHelper = new XmlSerializer(typeof(MyConfigService));//StreamWriter xmlWriter = new StreamWriter("MyConfig.xml");//xmlHelper.Serialize(xmlWriter, myConfigService);//xmlWriter.Close();StreamReader xmlReader = new StreamReader("MyConfig.xml");var res = xmlHelper.Deserialize(xmlReader);Console.WriteLine(JsonConvert.SerializeObject(res));Console.WriteLine("运行完成!");Console.ReadKey();}

在这里插入图片描述

在这里插入图片描述

简单的生成配置

C# XML序列化/反序列化参考

修改配置类

    /// <summary>/// 重命名根节点/// </summary>[XmlRoot("RootTest")]public class MyConfigService{/// <summary>/// 重命名,从Name变成extra/// </summary>[XmlElement("extra")]public string Name { get; set; }public string Description { get; set; }public int Id { get; set; }public bool IsEnabled { get; set; }public enum MyKeys { Apple,Banana,Pear}public MyKeys SettingKey { get; set; }/// <summary>/// 以Default属性的形式加载到根节点上面/// </summary>[XmlAttribute()]public string Default = "描述";public MyConfigService() { }}

测试用例

  static void Main(string[] args){MyConfigService myConfigService = new MyConfigService(){Name = "坤坤",Description = "偶像练习生",Id = 114514,IsEnabled = false,SettingKey = MyConfigService.MyKeys.Pear};var xmlHelper = new XmlSerializer(typeof(MyConfigService));StreamWriter xmlWriter = new StreamWriter("MyConfig.xml");//去掉烦人的命名空间XmlSerializerNamespaces ns = new XmlSerializerNamespaces();ns.Add("", "");xmlHelper.Serialize(xmlWriter, myConfigService,ns);xmlWriter.Close();//StreamReader xmlReader = new StreamReader("MyConfig.xml");//var res = xmlHelper.Deserialize(xmlReader);//Console.WriteLine(JsonConvert.SerializeObject(res));Console.WriteLine("运行完成!");Console.ReadKey();}

运行结果对比

在这里插入图片描述

代码逻辑封装

逻辑示意

xml序列化Helper
默认生成
默认读取
读取解析出错覆盖
文件路径是否存在确认

封装好的代码

   public class MyXmlConfigHelper<T>{public T Setting { get; set; }public string FileName { get; set; } = "MyConfig.xml";public string DirectoryPath{get{var regex = new Regex(@"\\(\w+)\.(\w+)$");return regex.Split(FullPath)[0];}}public string DebugPath { get => Directory.GetCurrentDirectory(); }public string FullPath { get => DebugPath + "\\" + FileName; }public bool IsFileExist { get => File.Exists(FullPath); }public bool IsDirectoryExist { get => Directory.Exists(DirectoryPath); }public Action<string> ShowMsg { get; set; } = (msg)=>Console.WriteLine(msg);public MyXmlConfigHelper(){}public MyXmlConfigHelper(string filename){FileName = filename;if (!IsDirectoryExist){DirectoryInfo directoryInfo = new DirectoryInfo(DirectoryPath);directoryInfo.Create();}}public MyXmlConfigHelper(T setting ,string filename):this(filename){Setting = setting;}/// <summary>/// 创建文件/// </summary>public void Init(){if(IsFileExist){try{Read();}catch (Exception ex){ShowMsg(ex.ToString());throw new Exception("文件读取失败!请确认是否配置文件格式是否正确");}}else{Write();}}/// <summary>/// 覆盖文件/// </summary>public void ReInit(){ShowMsg("正在覆盖配置文件:" + FullPath);Write();}/// <summary>/// 写入配置类/// </summary>private void Write(){ShowMsg("正在生成配置文件:" + FullPath);var xmlHelper = new XmlSerializer(typeof(T));using (StreamWriter xmlWriter = new StreamWriter(FullPath)){//去掉烦人的命名空间XmlSerializerNamespaces ns = new XmlSerializerNamespaces();ns.Add("", "");xmlHelper.Serialize(xmlWriter, Setting, ns);xmlWriter.Close();}}/// <summary>/// 读取配置类/// </summary>private void Read(){ShowMsg("正在读取配置文件:"+FullPath);var xmlHelper = new XmlSerializer(typeof(T));using (StreamReader xmlReader = new StreamReader(FullPath)){Setting = (T)xmlHelper.Deserialize(xmlReader);xmlReader.Close();}}}

测试生成

    static void Main(string[] args){var config = new MyConfigService() {Name = "小坤",Description="爱坤",Default = "鲲鲲",SettingKey = MyConfigService.MyKeys.Banana,Id = 80086,IsEnabled = true,};var xmlHelper = new MyXmlConfigHelper<MyConfigService>(config, "Config\\MyConfig.xml");xmlHelper.Init();Console.WriteLine("运行完成!");Console.ReadKey();}

在这里插入图片描述
我还做了判断,如果不存在,则生成默认,如果存在,则读取的判断

配置文件格式错误测试

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

使用默认值覆盖来解决问题

    static void Main(string[] args){var config = new MyConfigService() {Name = "小坤",Description="爱坤",Default = "鲲鲲",SettingKey = MyConfigService.MyKeys.Banana,Id = 80086,IsEnabled = true,};var xmlHelper = new MyXmlConfigHelper<MyConfigService>(config, "Config\\MyConfig.xml");try{xmlHelper.Init();}catch (Exception ex){//如果出错,则使用默认值覆盖Console.WriteLine(ex.ToString());xmlHelper.ReInit();}Console.WriteLine("运行完成!");Console.ReadKey();}

在这里插入图片描述

配置文件人为修改错误如何解决

解决方案有以下几种

  • 不解决,使用默认值,一直到人为修改回去
  • 手动解决,但是现场人员不一定了解配置信息
  • 重新生成覆盖,但是这样会丢失以前配置的数据
  • 从缓存数据库中读取上传成功运行的代码,回复到最初的状态

理论上来说,第4个是最好的,因为我们现场人员就算修改出现问题了,也能回滚到程序之前的配置。但是C# 默认是没有缓存这个东西的。缓存是需要存在一个地方。我个人认为最好的存储中介就是Sqlite数据库。Sqlite本身体积小,性能强,不需要安装。对于1G以下,100万条以下的数据最好的存储中介。

挖个坑,后面研究一下基于Sqlite的缓存数据库

解决方案

生成两个配置文件,一个是主配置文件,一个是备份配置文件。

程序运行
读取主要+备份
主要+备份都完整
主要覆盖备份
主要破损,备份完整
备份还原主要
主要完整,备份破损
主要+备份都破损
主要备份默认值覆盖

代码

    public class MyXmlConfigAutoHelper<T>{public T Setting { get; set; }public string FileName { get; set; } = "MyConfig.xml";public string BackupName{get{var regex = new Regex(@"(\w+)\.(\w+)$");var filename = regex.Match(FileName).Value;var backName = (new Regex(@"\.(\w+)$")).Split(filename)[0];var newBackName = backName + "_back";return (new Regex(backName)).Replace(FileName, newBackName);}}/// <summary>/// 备份/// </summary>private MyXmlConfigHelper<T> backupXml { get; set; }private MyXmlConfigHelper<T> settingXml { get; set; }public MyXmlConfigAutoHelper(){}public MyXmlConfigAutoHelper(string fileName){FileName = fileName;}public MyXmlConfigAutoHelper(string fileName, T setting){Setting = setting;FileName = fileName;}/// <summary>/// 实例化/// </summary>public void AutoInit(){settingXml = new MyXmlConfigHelper<T>(Setting, FileName);backupXml = new MyXmlConfigHelper<T>(Setting, BackupName);//如果备份也损坏了,就GG了var settingFlag = true;var backupFlag = true;try{settingXml.Init();}catch (Exception ex){Console.WriteLine("主文件读取失败");Console.WriteLine(ex.Message);settingFlag = false;}try{backupXml.Init();}catch (Exception ex){Console.WriteLine("备份文件读取失败");Console.WriteLine(ex.Message);backupFlag = false;}Console.WriteLine($"文件完整性:setting[{settingFlag},backup[{backupFlag}]]");if (!backupFlag && !settingFlag){Console.WriteLine("主要和备份文件完全破损,默认值覆盖");backupXml.ReInit();settingXml.ReInit();}else if (!backupFlag){Console.WriteLine("备份文件完全破损,主要文件覆盖");backupXml.Setting = settingXml.Setting;backupXml.ReInit();}else if (!settingFlag){Console.WriteLine("主要文件完全破损,备份文件覆盖");settingXml.Setting = backupXml.Setting;settingXml.ReInit();}else{Console.WriteLine("主要和备份文件正常,主要文件覆盖备份文件");backupXml.Setting = settingXml.Setting;backupXml.ReInit();}Setting = settingXml.Setting;}public void ReInit(){settingXml = new MyXmlConfigHelper<T>(Setting, FileName);backupXml = new MyXmlConfigHelper<T>(Setting, BackupName);settingXml.ReInit();backupXml.ReInit();}}

测试用例

        static void Main(string[] args){var config = new MyConfigService() {Name = "小坤",Description="爱坤",Default = "鲲鲲",SettingKey = MyConfigService.MyKeys.Banana,Id = 80086,IsEnabled = true,};var xmlAutoHelper = new MyXmlConfigAutoHelper<MyConfigService>("resource\\Myconfig.xml", config);xmlAutoHelper.AutoInit();//Console.WriteLine(xmlAutoHelper.BackupName);Console.WriteLine("运行完成!");Console.ReadKey();}

运行结果

由于测试步骤过于复杂,情况比较多,这里就不放截图了。简单来说就是尽可能的使用已有的数据进行还原,如果两个文件都损坏直接使用默认值替换

代码封装总结

    public class MyXmlConfigAutoHelper<T>{public T Setting { get; set; }public string FileName { get; set; } = "MyConfig.xml";public string BackupName{get{var regex = new Regex(@"(\w+)\.(\w+)$");var filename = regex.Match(FileName).Value;var backName = (new Regex(@"\.(\w+)$")).Split(filename)[0];var newBackName = backName + "_back";return (new Regex(backName)).Replace(FileName, newBackName);}}/// <summary>/// 备份/// </summary>private MyXmlConfigHelper<T> backupXml { get; set; }private MyXmlConfigHelper<T> settingXml { get; set; }public MyXmlConfigAutoHelper(){}public MyXmlConfigAutoHelper(string fileName){FileName = fileName;}public MyXmlConfigAutoHelper(string fileName, T setting){Setting = setting;FileName = fileName;}/// <summary>/// 实例化/// </summary>public void AutoInit(){settingXml = new MyXmlConfigHelper<T>(Setting, FileName);backupXml = new MyXmlConfigHelper<T>(Setting, BackupName);//如果备份也损坏了,就GG了var settingFlag = true;var backupFlag = true;try{settingXml.Init();}catch (Exception ex){Console.WriteLine("主文件读取失败");Console.WriteLine(ex.Message);settingFlag = false;}try{backupXml.Init();}catch (Exception ex){Console.WriteLine("备份文件读取失败");Console.WriteLine(ex.Message);backupFlag = false;}Console.WriteLine($"文件完整性:setting[{settingFlag},backup[{backupFlag}]]");if (!backupFlag && !settingFlag){Console.WriteLine("主要和备份文件完全破损,默认值覆盖");backupXml.ReInit();settingXml.ReInit();}else if (!backupFlag){Console.WriteLine("备份文件完全破损,主要文件覆盖");backupXml.Setting = settingXml.Setting;backupXml.ReInit();}else if (!settingFlag){Console.WriteLine("主要文件完全破损,备份文件覆盖");settingXml.Setting = backupXml.Setting;settingXml.ReInit();}else{Console.WriteLine("主要和备份文件正常,主要文件覆盖备份文件");backupXml.Setting = settingXml.Setting;backupXml.ReInit();}Setting = settingXml.Setting;}public void ReInit(){settingXml = new MyXmlConfigHelper<T>(Setting, FileName);backupXml = new MyXmlConfigHelper<T>(Setting, BackupName);settingXml.ReInit();backupXml.ReInit();}}public class MyXmlConfigHelper<T>{public T Setting { get; set; }public string FileName { get; set; } = "MyConfig.xml";public string DirectoryPath{get{var regex = new Regex(@"\\(\w+)\.(\w+)$");return regex.Split(FullPath)[0];}}public string DebugPath { get => Directory.GetCurrentDirectory(); }public string FullPath { get => DebugPath + "\\" + FileName; }public bool IsFileExist { get => File.Exists(FullPath); }public bool IsDirectoryExist { get => Directory.Exists(DirectoryPath); }public Action<string> ShowMsg { get; set; } = (msg) => Console.WriteLine(msg);public MyXmlConfigHelper(){}public MyXmlConfigHelper(string filename){FileName = filename;if (!IsDirectoryExist){DirectoryInfo directoryInfo = new DirectoryInfo(DirectoryPath);directoryInfo.Create();}}public MyXmlConfigHelper(T setting, string filename) : this(filename){Setting = setting;}/// <summary>/// 创建文件/// </summary>public void Init(){if (IsFileExist){try{Read();}catch (Exception ex){ShowMsg(ex.ToString());throw new Exception("文件读取失败!请确认是否配置文件格式是否正确");}}else{Write();}}/// <summary>/// 覆盖文件/// </summary>public void ReInit(){ShowMsg("正在覆盖配置文件:" + FullPath);Write();}/// <summary>/// 写入配置类/// </summary>public void Write(){ShowMsg("正在生成配置文件:" + FullPath);var xmlHelper = new XmlSerializer(typeof(T));using (StreamWriter xmlWriter = new StreamWriter(FullPath)){//去掉烦人的命名空间XmlSerializerNamespaces ns = new XmlSerializerNamespaces();ns.Add("", "");xmlHelper.Serialize(xmlWriter, Setting, ns);xmlWriter.Close();}}/// <summary>/// 读取配置类/// </summary>public void Read(){ShowMsg("正在读取配置文件:" + FullPath);var xmlHelper = new XmlSerializer(typeof(T));using (StreamReader xmlReader = new StreamReader(FullPath)){Setting = (T)xmlHelper.Deserialize(xmlReader);xmlReader.Close();}}}

总结

我这里最后加了个back备份文件,我们平时就修改主要文件的配置即可。如果主要文件损坏,那就备份文件补上。但是这个是主要文件损坏的情况,如果主要文件没损坏,是参数设置错了呢?那我们可以自动生成按照时间戳的备份文件,一次存多个。

  • 主要文件
  • 备份文件-2024-1-6 17:37:20
  • 备份文件-2024-1-6 17:37:30

为了安全考虑的方式是没有上限的。这里就不展开说明了,这里已经写好一个基本的设置文件自动保存,和设置文件自动备份回档的功能,如果想要更高的安全基本可以自己在我的代码上面继续封装。

还有备份文件可以当做缓存文件一样来使用,但是这个是明文存储的。可以自己手动加密一下,反正加密和解密的方法也有很多。可以自己琢磨一下。

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Hadoop集群三节点搭建(二)

一、克隆三台主机&#xff08;hadoop102 hadoop103 hadoop104&#xff09; 以master为样板机克隆三台出来&#xff0c;克隆前先把master关机 按照上面的步骤克隆其他两个就可以了&#xff0c;记得修改ip和hostname 二、编写集群同步脚本 在/home/attest/ 创建bin目录&…

一个H3C交换机周期性断网并自动恢复的排查案例

一个朋友发我一个H3C日志&#xff0c;这个交换机是汇聚层交换机&#xff0c;1和2口是trunk口&#xff0c;其它接口是access接口&#xff0c;17-21口据说接的都是监控、终端。日志里面看到大量的拓朴改变&#xff0c;好几个网口up、down的日志&#xff0c;怀疑是环路&#xff0c…

Adboost算法

1描述 AdaBoost算法每次都是使用全部的样本进行训练&#xff0c;每一轮训练结束后&#xff0c;得到一个基学习器&#xff0c;并计算该基学习器在训练样本的预测误差率&#xff0c;然后根据这个误差率来更新下一轮训练时训练集合样本的权重系数和本轮基学习器的投票权重&#x…

Spring Boot实现数据加密脱敏:注解 + 反射 + AOP

文章目录 1. 引言2. 数据加密和脱敏的需求3. Spring Boot项目初始化4. 敏感数据加密注解设计5. 实现加密和脱敏的工具类6. 实体类和加密脱敏注解的使用7. 利用AOP实现加密和脱敏8. 完善AOP切面9. 测试10. 拓展功能与未来展望10.1 加密算法的选择10.2 动态注解配置 11. 总结 &am…

三剑客前端教程

前端教程 结构层&#xff08;html&#xff09;表现层&#xff08;css&#xff09;行为层&#xff08;javascript&#xff09; HTML 超文本标记语言&#xff09; HTML&#xff08;超文本标记语言——HyperText Markup Language&#xff09;是构成 Web 世界的一砖一瓦。它定义…

云服务器ECS搭建个人项目

一、登录云服务器ECS 在ECS实例的操作列中点击远程连接云服务器ECS&#xff0c;点击实例最右侧的远程连接按钮&#xff0c;并立即登录后会跳转至Workbench的登录页面。但是第一次进去不知道密码&#xff1f;可以重置密码 登录后可以看到如下页面&#xff0c;说明已经成功登录到…

【数据结构】树的遍历

树的遍历 前序遍历 前序遍历是按照根节点->左子树->右子树的顺序进行遍历 图片来源维基百科深度优先遍历&#xff08;前序遍历&#xff09;: F, B, A, D, C, E, G, I, H. 代码实现 递归 # class TreeNode: # def __init__(self, x): # self.val x # …

RFID标签在汽车监管方面的应用与实施方案

RFID技术在汽车工业领域得到了广泛应用&#xff0c;主要体现在汽车资质证书远程监管系统的普及化&#xff0c;系统包括OBD接口监视器、车证监管箱、超高频读写设备、应用系统软件以及大数据采集与处理等组成部分。 在汽车物流监管方面&#xff0c;系统利用OBD接口监控车辆并实时…

idea中使用Lombok 失效,@Slf4j 找不到符号的解决办法

文章目录 一、前言二、问题排查和解决方案三、 其他解决方案3.1 另一种解决方案3.2 参考文章 一、前言 今天在一个多module工程中&#xff0c;新增了一个 springboot&#xff08;版本 2.2.4.RELEASE&#xff09; module&#xff0c;像往常一样&#xff0c;我引入了lombok依赖&…

Nacos 持久化及集群的搭建【微服务】

文章目录 一、统一配置管理二、微服务配置拉取三、配置热更新四、多环境共享配置五、Nacos 集群搭建1. 集群结构2. 初始化数据库3. 搭建集群 六、Nginx 反向代理七、启动项目测试 一、统一配置管理 案例练习的时候我们只有两个微服务&#xff0c;管理起来非常简单&#xff0c;但…

数据采集有哪些方法?HTTP代理起到什么作用?

在这个数字化的时代&#xff0c;数据就如同生活中不可或缺的元素&#xff0c;我们的行为、喜好、甚至是想法都被转化成了数字化的信息。那么&#xff0c;现代社会是如何进行数据的采集的呢&#xff1f;让我们一同来看看&#xff01; 1. 网络浏览行为的追踪 在我们浏览互联网的…