读取配置文件到Settings对象的完整实现
文章目录
- 1. 定义配置模型类
- 2. 创建示例config.json文件
- 3. 实现配置读取器
- 4. 使用示例
- 5. 扩展ModbusTcpHelper类
- 6. 配置验证增强版
- 7. 处理特殊需求
- 配置文件种类及其比较:JSON配置文件的优势
- 一、常见配置文件类型及示例
- 1. JSON (JavaScript Object Notation)
- 2. XML (eXtensible Markup Language)
- 3. YAML (YAML Ain't Markup Language)
- 4. INI (Initialization File)
- 5. 环境变量
- 6. 二进制格式
- 二、JSON配置文件的优势
- 1. 可读性强
- 2. 数据结构丰富
- 3. 跨平台兼容性好
- 4. 工具生态完善
- 5. 性能优异
- 6. 现代开发标准
- 7. 支持注释(通过变通方式)
- 三、各类型配置文件对比表
- 四、JSON配置的典型应用场景
- 1. 应用程序配置
- 2. IoT设备配置
- 3. 网络服务配置
- 五、JSON配置的最佳实践
- 六、何时不选择JSON
- 七、总结
下面我将展示如何从config.json文件读取配置并转换为强类型的Settings对象,使用.NET 8和System.Text.Json。
1. 定义配置模型类
首先创建表示配置结构的模型类:
public class ModbusSettings
{public string IpAddress { get; set; }public int Port { get; set; }public byte SlaveId { get; set; }public int TimeoutMs { get; set; } = 5000;public List<RegisterMap> RegisterMaps { get; set; }
}public class RegisterMap
{public string Name { get; set; }public ushort Address { get; set; }public DataType DataType { get; set; }public float ScalingFactor { get; set; } = 1.0f;
}public enum DataType
{UInt16,Int16,UInt32,Int32,Float
}
2. 创建示例config.json文件
{"IpAddress": "192.168.1.202","Port": 4196,"SlaveId": 40,"TimeoutMs": 3000,"RegisterMaps": [{"Name": "Temperature","Address": 22,"DataType": "Float","ScalingFactor": 0.1},{"Name": "Pressure","Address": 26,"DataType": "Float"}]
}
3. 实现配置读取器
using System.Text.Json;
using System.Text.Json.Serialization;public static class ConfigLoader
{private static readonly JsonSerializerOptions _options = new(){PropertyNameCaseInsensitive = true,Converters = { new JsonStringEnumConverter() },AllowTrailingCommas = true,ReadCommentHandling = JsonCommentHandling.Skip};public static ModbusSettings LoadConfig(string filePath = "config.json"){try{if (!File.Exists(filePath)){throw new FileNotFoundException($"配置文件 {filePath} 不存在");}string json = File.ReadAllText(filePath);var settings = JsonSerializer.Deserialize<ModbusSettings>(json, _options);if (settings == null){throw new InvalidOperationException("配置文件内容为空或格式不正确");}// 验证必要配置if (string.IsNullOrWhiteSpace(settings.IpAddress)){throw new InvalidDataException("IP地址不能为空");}if (settings.Port <= 0 || settings.Port > 65535){throw new InvalidDataException("端口号必须在1-65535范围内");}return settings;}catch (JsonException ex){throw new InvalidOperationException($"配置文件解析失败: {ex.Message}", ex);}}// 可选:保存配置的方法public static void SaveConfig(ModbusSettings settings, string filePath = "config.json"){var options = new JsonSerializerOptions{WriteIndented = true,Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping,Converters = { new JsonStringEnumConverter() }};string json = JsonSerializer.Serialize(settings, options);File.WriteAllText(filePath, json);}
}
4. 使用示例
class Program
{static async Task Main(){try{// 加载配置var settings = ConfigLoader.LoadConfig();Console.WriteLine($"成功加载配置: {settings.IpAddress}:{settings.Port}");// 使用配置初始化Modbus帮助类var modbusHelper = new ModbusTcpHelper(settings.IpAddress, settings.Port);await modbusHelper.ConnectAsync(settings.TimeoutMs);// 读取配置中定义的寄存器foreach (var map in settings.RegisterMaps){try{// 根据数据类型读取数据object value = map.DataType switch{DataType.Float => await ReadFloatRegister(modbusHelper, settings.SlaveId, map),_ => await ReadStandardRegister(modbusHelper, settings.SlaveId, map)};// 应用缩放因子if (value is float floatValue && map.ScalingFactor != 1.0f){value = floatValue * map.ScalingFactor;}Console.WriteLine($"{map.Name} ({map.Address}): {value}");}catch (Exception ex){Console.WriteLine($"读取 {map.Name} 失败: {ex.Message}");}}}catch (Exception ex){Console.WriteLine($"发生错误: {ex.Message}");}}private static async Task<object> ReadFloatRegister(ModbusTcpHelper helper, byte slaveId, RegisterMap map){// 浮点数需要读取2个寄存器(4字节)float[] values = await helper.ReadFloatRegistersAsync(slaveId, map.Address, 2);return values[0];}private static async Task<object> ReadStandardRegister(ModbusTcpHelper helper, byte slaveId, RegisterMap map){// 读取单个寄存器ushort[] values = await helper.ReadRegistersAsync(slaveId, map.Address, 1);return map.DataType switch{DataType.UInt16 => values[0],DataType.Int16 => (short)values[0],_ => throw new NotSupportedException($"不支持的数据类型: {map.DataType}")};}
}
5. 扩展ModbusTcpHelper类
在之前的ModbusTcpHelper类中添加以下方法以支持更多数据类型:
/// <summary>
/// 读取标准寄存器值
/// </summary>
public async Task<ushort[]> ReadRegistersAsync(byte slaveId, ushort startAddress, ushort registerCount)
{byte[] request = new byte[6];request[0] = slaveId;request[1] = 0x03; // 功能码: 读保持寄存器request[2] = (byte)(startAddress >> 8);request[3] = (byte)(startAddress & 0xFF);request[4] = (byte)(registerCount >> 8);request[5] = (byte)(registerCount & 0xFF);byte[] response = await SendRequestAsync(request);// 提取寄存器数据(每个寄存器2字节)ushort[] registers = new ushort[registerCount];for (int i = 0; i < registerCount; i++){int offset = 3 + i * 2;registers[i] = (ushort)((response[offset] << 8) | response[offset + 1]);}return registers;
}
6. 配置验证增强版
可以添加更详细的配置验证:
public class ModbusSettingsValidator
{public static void Validate(ModbusSettings settings){if (settings == null)throw new ArgumentNullException(nameof(settings));if (string.IsNullOrWhiteSpace(settings.IpAddress))throw new InvalidDataException("IP地址不能为空");if (!IPAddress.TryParse(settings.IpAddress, out _))throw new InvalidDataException("IP地址格式不正确");if (settings.Port < 1 || settings.Port > 65535)throw new InvalidDataException("端口号必须在1-65535范围内");if (settings.SlaveId < 1 || settings.SlaveId > 247)throw new InvalidDataException("从站ID必须在1-247范围内");if (settings.TimeoutMs < 100)throw new InvalidDataException("超时时间不能小于100ms");if (settings.RegisterMaps == null || settings.RegisterMaps.Count == 0)throw new InvalidDataException("至少需要配置一个寄存器映射");foreach (var map in settings.RegisterMaps){if (string.IsNullOrWhiteSpace(map.Name))throw new InvalidDataException("寄存器名称不能为空");if (map.Address > 65535)throw new InvalidDataException($"寄存器地址 {map.Name} 超出范围");if (map.ScalingFactor == 0)throw new InvalidDataException($"缩放因子 {map.Name} 不能为零");}}
}// 在ConfigLoader中使用
var settings = JsonSerializer.Deserialize<ModbusSettings>(json, _options);
ModbusSettingsValidator.Validate(settings);
7. 处理特殊需求
如果需要支持更复杂的配置,如:
- 自定义字节序:在RegisterMap中添加ByteOrder属性
- 位操作:支持读取线圈和离散输入
- 批量读取优化:配置中定义哪些寄存器可以批量读取
示例扩展配置:
{"BatchReads": [{"Name": "SensorGroup1","StartAddress": 22,"Count": 4,"MapsTo": ["Temperature", "Humidity", "Pressure", "Voltage"]}]
}
这种架构提供了灵活的配置方式,同时保持了类型安全和良好的错误处理。
配置文件种类及其比较:JSON配置文件的优势
一、常见配置文件类型及示例
1. JSON (JavaScript Object Notation)
示例:
{"appSettings": {"name": "数据采集系统","version": "1.2.0","debugMode": false,"sampleRate": 1000},"modbus": {"ip": "192.168.1.202","port": 502,"timeout": 3000}
}
2. XML (eXtensible Markup Language)
示例:
<configuration><appSettings><name>数据采集系统</name><version>1.2.0</version><debugMode>false</debugMode><sampleRate>1000</sampleRate></appSettings><modbus><ip>192.168.1.202</ip><port>502</port><timeout>3000</timeout></modbus>
</configuration>
3. YAML (YAML Ain’t Markup Language)
示例:
appSettings:name: "数据采集系统"version: "1.2.0"debugMode: falsesampleRate: 1000modbus:ip: "192.168.1.202"port: 502timeout: 3000
4. INI (Initialization File)
示例:
[appSettings]
name=数据采集系统
version=1.2.0
debugMode=false
sampleRate=1000[modbus]
ip=192.168.1.202
port=502
timeout=3000
5. 环境变量
示例:
APP_NAME=数据采集系统
APP_VERSION=1.2.0
MODBUS_IP=192.168.1.202
MODBUS_PORT=502
6. 二进制格式
示例:通常不可读,如Windows注册表、Protocol Buffers等
二、JSON配置文件的优势
1. 可读性强
JSON采用键值对结构和缩进格式,比XML更简洁:
{"user": {"name": "张三","age": 30,"active": true}
}
对比XML:
<user><name>张三</name><age>30</age><active>true</active>
</user>
2. 数据结构丰富
支持多种数据类型:
- 对象(嵌套结构)
- 数组
- 字符串
- 数字
- 布尔值
- null
{"devices": [{"id": 1,"type": "sensor","registers": [40001, 40002, 40003]},{"id": 2,"type": "actuator","enabled": true}]
}
3. 跨平台兼容性好
- 所有现代编程语言都内置JSON支持
- Web应用天然支持
- 移动端和嵌入式系统也能轻松处理
4. 工具生态完善
- 验证工具:JSON Schema验证
- 格式化工具:VS Code等编辑器内置支持
- 转换工具:可轻松转换为其他格式
- 在线工具:各种JSON在线解析和美化工具
5. 性能优异
- 解析速度比XML快
- 比YAML更高效
- 体积通常比XML小30%-50%
6. 现代开发标准
- REST API的标准数据格式
- NoSQL数据库(如MongoDB)使用类似JSON的BSON
- 前端框架(React/Vue等)直接支持
7. 支持注释(通过变通方式)
虽然标准JSON不支持注释,但可以通过以下方式实现:
{"//note": "这是设备配置","device": {"ip": "192.168.1.100","//status": "active|inactive","status": "active"}
}
三、各类型配置文件对比表
特性 | JSON | XML | YAML | INI | 环境变量 | 二进制 |
---|---|---|---|---|---|---|
可读性 | ★★★★★ | ★★★☆☆ | ★★★★★ | ★★★☆☆ | ★★☆☆☆ | ☆☆☆☆☆ |
数据结构 | ★★★★★ | ★★★★★ | ★★★★★ | ★★☆☆☆ | ★☆☆☆☆ | ★★★★★ |
跨平台支持 | ★★★★★ | ★★★★★ | ★★★★☆ | ★★★☆☆ | ★★★★★ | ★★★☆☆ |
工具生态 | ★★★★★ | ★★★★★ | ★★★★☆ | ★★★☆☆ | ★★★☆☆ | ★★☆☆☆ |
解析性能 | ★★★★★ | ★★★☆☆ | ★★★☆☆ | ★★★★★ | ★★★★★ | ★★★★★ |
适合场景 | 通用 | 复杂结构 | 人工维护 | 简单配置 | 容器化 | 高性能 |
四、JSON配置的典型应用场景
1. 应用程序配置
{"logging": {"level": "debug","path": "/var/log/app.log"},"database": {"connectionString": "Server=db;Database=appdb","timeout": 30}
}
2. IoT设备配置
{"deviceId": "SN-12345","sensors": [{"type": "temperature","address": 40001,"unit": "°C"},{"type": "humidity","address": 40003,"scale": 0.1}]
}
3. 网络服务配置
{"endpoints": {"api": "https://api.example.com/v1","auth": "https://auth.example.com"},"retryPolicy": {"maxAttempts": 3,"delay": 1000}
}
五、JSON配置的最佳实践
-
使用有意义的键名:
// 不好 "t": 30// 好 "temperature": 30
-
合理组织层次结构:
{"modbus": {"tcp": {"ip": "192.168.1.100","port": 502},"rtu": {"port": "COM3","baudRate": 9600}} }
-
添加配置版本控制:
{"configVersion": "1.1","settings": {// ...} }
-
为重要配置添加默认值:
{"timeout": 5000, // 默认5秒"retries": 3 // 默认3次 }
-
使用JSON Schema验证:
{"$schema": "./config-schema.json",// 实际配置内容 }
六、何时不选择JSON
虽然JSON有很多优点,但在以下情况可能需要考虑其他方案:
- 需要注释:考虑YAML
- 极简配置:考虑INI或环境变量
- 高性能场景:考虑二进制格式
- 人工编辑频繁:考虑YAML(更友好的格式)
七、总结
JSON作为配置文件格式具有以下核心优势:
- 完美的平衡性:在可读性、功能性和性能之间取得最佳平衡
- 语言无关性:几乎所有现代编程语言都原生支持
- 层次化结构:能清晰表达复杂配置关系
- 扩展性强:支持数组、嵌套对象等复杂数据结构
- 工具链完善:从编辑器支持到验证工具一应俱全
对于大多数应用场景,特别是需要表达结构化数据、跨平台使用或与现代Web技术栈集成的场景,JSON都是配置文件的最佳选择。