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

本文主要是介绍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

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

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

这篇关于C# 自定义配置文件序列化生成+文件格式错误自动回档的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/577260

相关文章

Java controller接口出入参时间序列化转换操作方法(两种)

《Javacontroller接口出入参时间序列化转换操作方法(两种)》:本文主要介绍Javacontroller接口出入参时间序列化转换操作方法,本文给大家列举两种简单方法,感兴趣的朋友一起看... 目录方式一、使用注解方式二、统一配置场景:在controller编写的接口,在前后端交互过程中一般都会涉及

Python实现微信自动锁定工具

《Python实现微信自动锁定工具》在数字化办公时代,微信已成为职场沟通的重要工具,但临时离开时忘记锁屏可能导致敏感信息泄露,下面我们就来看看如何使用Python打造一个微信自动锁定工具吧... 目录引言:当微信隐私遇到自动化守护效果展示核心功能全景图技术亮点深度解析1. 无操作检测引擎2. 微信路径智能获

C#如何调用C++库

《C#如何调用C++库》:本文主要介绍C#如何调用C++库方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录方法一:使用P/Invoke1. 导出C++函数2. 定义P/Invoke签名3. 调用C++函数方法二:使用C++/CLI作为桥接1. 创建C++/CL

Spring Security自定义身份认证的实现方法

《SpringSecurity自定义身份认证的实现方法》:本文主要介绍SpringSecurity自定义身份认证的实现方法,下面对SpringSecurity的这三种自定义身份认证进行详细讲解,... 目录1.内存身份认证(1)创建配置类(2)验证内存身份认证2.JDBC身份认证(1)数据准备 (2)配置依

C#使用StackExchange.Redis实现分布式锁的两种方式介绍

《C#使用StackExchange.Redis实现分布式锁的两种方式介绍》分布式锁在集群的架构中发挥着重要的作用,:本文主要介绍C#使用StackExchange.Redis实现分布式锁的... 目录自定义分布式锁获取锁释放锁自动续期StackExchange.Redis分布式锁获取锁释放锁自动续期分布式

SpringBoot中配置文件的加载顺序解读

《SpringBoot中配置文件的加载顺序解读》:本文主要介绍SpringBoot中配置文件的加载顺序,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录SpringBoot配置文件的加载顺序1、命令⾏参数2、Java系统属性3、操作系统环境变量5、项目【外部】的ap

Spring Boot读取配置文件的五种方式小结

《SpringBoot读取配置文件的五种方式小结》SpringBoot提供了灵活多样的方式来读取配置文件,这篇文章为大家介绍了5种常见的读取方式,文中的示例代码简洁易懂,大家可以根据自己的需要进... 目录1. 配置文件位置与加载顺序2. 读取配置文件的方式汇总方式一:使用 @Value 注解读取配置方式二

C++如何通过Qt反射机制实现数据类序列化

《C++如何通过Qt反射机制实现数据类序列化》在C++工程中经常需要使用数据类,并对数据类进行存储、打印、调试等操作,所以本文就来聊聊C++如何通过Qt反射机制实现数据类序列化吧... 目录设计预期设计思路代码实现使用方法在 C++ 工程中经常需要使用数据类,并对数据类进行存储、打印、调试等操作。由于数据类

C# foreach 循环中获取索引的实现方式

《C#foreach循环中获取索引的实现方式》:本文主要介绍C#foreach循环中获取索引的实现方式,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录一、手动维护索引变量二、LINQ Select + 元组解构三、扩展方法封装索引四、使用 for 循环替代

C# Where 泛型约束的实现

《C#Where泛型约束的实现》本文主要介绍了C#Where泛型约束的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录使用的对象约束分类where T : structwhere T : classwhere T : ne