如何使用Microsoft.Extensions.Configuration读取含Name属性的配置节点
读取含Name属性的XML配置节点问题
问题场景
我在应用中尝试读取内存中的XML配置,配置包含3个节点,但目前只能读取不含Name属性的节点值。
代码实现
XML配置与读取代码
const string content = @"<?xml version=""1.0"" encoding=""utf-8""?> <configuration> <node1 Name=""something"" Foo=""Bar"" /> <node2 NoName=""something"" Foo=""Bar"" /> <node3 Name=""ignored"" NoName=""something"" Foo=""Bar"" /> </configuration>"; var doc = new XmlDocument(); doc.LoadXml(content); using var stream = new MemoryStream(); doc.Save(stream); stream.Position = 0; var configurationRoot = new ConfigurationBuilder() .AddXmlStream(stream) .Build(); var node1 = configurationRoot.GetSection("node1").Get<Node1>(); var node2 = configurationRoot.GetSection("node2").Get<Node2>(); var node3 = configurationRoot.GetSection("node3").Get<Node2>();
对应实体类代码
private class Node1 { public string Name { get; set; } public string Foo { get; set; } } private class Node2 { public string NoName { get; set; } public string Foo { get; set; } }
出现的问题
- node1包含Name属性,用Node1类调用
configurationRoot.GetSection("node1").Get<Node1>()无法填充值; - node2不含Name属性,用Node2类调用
configurationRoot.GetSection("node2").Get<Node2>()可正常填充值; - node3包含Name属性,用Node2类调用
configurationRoot.GetSection("node3").Get<Node2>()也无法填充任何值。
解决方案
在.NET的XML配置提供器中,Name属性被默认当作节点的名称标识符,而非普通属性。当节点存在Name属性时,配置提供器会将该节点解析为带键的子节点,而非将Name作为属性绑定到实体类。
方案1:修改XML配置的属性名(允许修改配置时优先使用)
将Name属性改为其他名称(比如NodeName),即可正常绑定到实体类的对应属性:
<node1 NodeName="something" Foo="Bar" />
对应的实体类调整为:
private class Node1 { public string NodeName { get; set; } public string Foo { get; set; } }
方案2:自定义XML配置提供器的解析逻辑(无法修改配置时使用)
通过自定义XmlConfigurationProvider,重写解析逻辑,忽略Name属性作为节点键的默认行为,将其当作普通属性处理:
public class CustomXmlConfigurationProvider : XmlConfigurationProvider { public CustomXmlConfigurationProvider(XmlConfigurationSource source) : base(source) { } protected override void Load(Stream stream) { var doc = new XmlDocument(); doc.Load(stream); Data = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); TraverseXmlNodes(doc.DocumentElement, null); } private void TraverseXmlNodes(XmlNode node, string path) { if (node.Attributes != null) { foreach (XmlAttribute attr in node.Attributes) { // 将Name作为普通属性加入配置,不做特殊处理 var key = string.IsNullOrEmpty(path) ? $"{node.Name}:{attr.Name}" : $"{path}:{node.Name}:{attr.Name}"; Data[key] = attr.Value; } } foreach (XmlNode childNode in node.ChildNodes) { if (childNode.NodeType == XmlNodeType.Element) { var newPath = string.IsNullOrEmpty(path) ? node.Name : $"{path}:{node.Name}"; TraverseXmlNodes(childNode, newPath); } } } } public class CustomXmlConfigurationSource : XmlConfigurationSource { public override IConfigurationProvider Build(IConfigurationBuilder builder) { EnsureDefaults(builder); return new CustomXmlConfigurationProvider(this); } } // 使用自定义提供器构建配置 var configurationRoot = new ConfigurationBuilder() .Add(new CustomXmlConfigurationSource { Stream = stream }) .Build();
修改后,实体类就能正常绑定Name属性。
内容的提问来源于stack exchange,提问作者FFMG
相关产品推荐
相关产品推荐

