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

相关文章

Springboot的ThreadPoolTaskScheduler线程池轻松搞定15分钟不操作自动取消订单

《Springboot的ThreadPoolTaskScheduler线程池轻松搞定15分钟不操作自动取消订单》:本文主要介绍Springboot的ThreadPoolTaskScheduler线... 目录ThreadPoolTaskScheduler线程池实现15分钟不操作自动取消订单概要1,创建订单后

解决mybatis-plus-boot-starter与mybatis-spring-boot-starter的错误问题

《解决mybatis-plus-boot-starter与mybatis-spring-boot-starter的错误问题》本文主要讲述了在使用MyBatis和MyBatis-Plus时遇到的绑定异常... 目录myBATis-plus-boot-starpythonter与mybatis-spring-b

python实现自动登录12306自动抢票功能

《python实现自动登录12306自动抢票功能》随着互联网技术的发展,越来越多的人选择通过网络平台购票,特别是在中国,12306作为官方火车票预订平台,承担了巨大的访问量,对于热门线路或者节假日出行... 目录一、遇到的问题?二、改进三、进阶–展望总结一、遇到的问题?1.url-正确的表头:就是首先ur

C#实现文件读写到SQLite数据库

《C#实现文件读写到SQLite数据库》这篇文章主要为大家详细介绍了使用C#将文件读写到SQLite数据库的几种方法,文中的示例代码讲解详细,感兴趣的小伙伴可以参考一下... 目录1. 使用 BLOB 存储文件2. 存储文件路径3. 分块存储文件《文件读写到SQLite数据库China编程的方法》博客中,介绍了文

使用C#如何创建人名或其他物体随机分组

《使用C#如何创建人名或其他物体随机分组》文章描述了一个随机分配人员到多个团队的代码示例,包括将人员列表随机化并根据组数分配到不同组,最后按组号排序显示结果... 目录C#创建人名或其他物体随机分组此示例使用以下代码将人员分配到组代码首先将lstPeople ListBox总结C#创建人名或其他物体随机分组

在C#中合并和解析相对路径方式

《在C#中合并和解析相对路径方式》Path类提供了几个用于操作文件路径的静态方法,其中包括Combine方法和GetFullPath方法,Combine方法将两个路径合并在一起,但不会解析包含相对元素... 目录C#合并和解析相对路径System.IO.Path类幸运的是总结C#合并和解析相对路径对于 C

Spring使用@Retryable实现自动重试机制

《Spring使用@Retryable实现自动重试机制》在微服务架构中,服务之间的调用可能会因为一些暂时性的错误而失败,例如网络波动、数据库连接超时或第三方服务不可用等,在本文中,我们将介绍如何在Sp... 目录引言1. 什么是 @Retryable?2. 如何在 Spring 中使用 @Retryable

C#中字符串分割的多种方式

《C#中字符串分割的多种方式》在C#编程语言中,字符串处理是日常开发中不可或缺的一部分,字符串分割是处理文本数据时常用的操作,它允许我们将一个长字符串分解成多个子字符串,本文给大家介绍了C#中字符串分... 目录1. 使用 string.Split2. 使用正则表达式 (Regex.Split)3. 使用

详解Java中如何使用JFreeChart生成甘特图

《详解Java中如何使用JFreeChart生成甘特图》甘特图是一种流行的项目管理工具,用于显示项目的进度和任务分配,在Java开发中,JFreeChart是一个强大的开源图表库,能够生成各种类型的图... 目录引言一、JFreeChart简介二、准备工作三、创建甘特图1. 定义数据集2. 创建甘特图3.

一文带你搞懂Nginx中的配置文件

《一文带你搞懂Nginx中的配置文件》Nginx(发音为“engine-x”)是一款高性能的Web服务器、反向代理服务器和负载均衡器,广泛应用于全球各类网站和应用中,下面就跟随小编一起来了解下如何... 目录摘要一、Nginx 配置文件结构概述二、全局配置(Global Configuration)1. w