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

相关文章

Python使用Tenacity一行代码实现自动重试详解

《Python使用Tenacity一行代码实现自动重试详解》tenacity是一个专为Python设计的通用重试库,它的核心理念就是用简单、清晰的方式,为任何可能失败的操作添加重试能力,下面我们就来看... 目录一切始于一个简单的 API 调用Tenacity 入门:一行代码实现优雅重试精细控制:让重试按我

SQL Server跟踪自动统计信息更新实战指南

《SQLServer跟踪自动统计信息更新实战指南》本文详解SQLServer自动统计信息更新的跟踪方法,推荐使用扩展事件实时捕获更新操作及详细信息,同时结合系统视图快速检查统计信息状态,重点强调修... 目录SQL Server 如何跟踪自动统计信息更新:深入解析与实战指南 核心跟踪方法1️⃣ 利用系统目录

Spring Security 单点登录与自动登录机制的实现原理

《SpringSecurity单点登录与自动登录机制的实现原理》本文探讨SpringSecurity实现单点登录(SSO)与自动登录机制,涵盖JWT跨系统认证、RememberMe持久化Token... 目录一、核心概念解析1.1 单点登录(SSO)1.2 自动登录(Remember Me)二、代码分析三、

C#中lock关键字的使用小结

《C#中lock关键字的使用小结》在C#中,lock关键字用于确保当一个线程位于给定实例的代码块中时,其他线程无法访问同一实例的该代码块,下面就来介绍一下lock关键字的使用... 目录使用方式工作原理注意事项示例代码为什么不能lock值类型在C#中,lock关键字用于确保当一个线程位于给定实例的代码块中时

C# $字符串插值的使用

《C#$字符串插值的使用》本文介绍了C#中的字符串插值功能,详细介绍了使用$符号的实现方式,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习吧... 目录$ 字符使用方式创建内插字符串包含不同的数据类型控制内插表达式的格式控制内插表达式的对齐方式内插表达式中使用转义序列内插表达式中使用

C#中的Converter的具体应用

《C#中的Converter的具体应用》C#中的Converter提供了一种灵活的类型转换机制,本文详细介绍了Converter的基本概念、使用场景,具有一定的参考价值,感兴趣的可以了解一下... 目录Converter的基本概念1. Converter委托2. 使用场景布尔型转换示例示例1:简单的字符串到

springboot自定义注解RateLimiter限流注解技术文档详解

《springboot自定义注解RateLimiter限流注解技术文档详解》文章介绍了限流技术的概念、作用及实现方式,通过SpringAOP拦截方法、缓存存储计数器,结合注解、枚举、异常类等核心组件,... 目录什么是限流系统架构核心组件详解1. 限流注解 (@RateLimiter)2. 限流类型枚举 (

MyBatis-Plus 自动赋值实体字段最佳实践指南

《MyBatis-Plus自动赋值实体字段最佳实践指南》MyBatis-Plus通过@TableField注解与填充策略,实现时间戳、用户信息、逻辑删除等字段的自动填充,减少手动赋值,提升开发效率与... 目录1. MyBATis-Plus 自动赋值概述1.1 适用场景1.2 自动填充的原理1.3 填充策略

C#监听txt文档获取新数据方式

《C#监听txt文档获取新数据方式》文章介绍通过监听txt文件获取最新数据,并实现开机自启动、禁用窗口关闭按钮、阻止Ctrl+C中断及防止程序退出等功能,代码整合于主函数中,供参考学习... 目录前言一、监听txt文档增加数据二、其他功能1. 设置开机自启动2. 禁止控制台窗口关闭按钮3. 阻止Ctrl +

SpringBoot 异常处理/自定义格式校验的问题实例详解

《SpringBoot异常处理/自定义格式校验的问题实例详解》文章探讨SpringBoot中自定义注解校验问题,区分参数级与类级约束触发的异常类型,建议通过@RestControllerAdvice... 目录1. 问题简要描述2. 异常触发1) 参数级别约束2) 类级别约束3. 异常处理1) 字段级别约束