【发布时间】:2019-02-27 22:29:19
【问题描述】:
我正在编写的 ASP.NET Core (2.1) 应用程序使用两个配置源:JSON 和 XML(使用框架的标准 AddJsonFile 和 AddXmlFile。XML 设置具有更高的优先级(例如,应该覆盖从JSON 文件)。
到目前为止一切正常,直到我不得不在 XML 文件中创建一个数组。有可能吗?
【问题讨论】:
标签: xml asp.net-core .net-core
我正在编写的 ASP.NET Core (2.1) 应用程序使用两个配置源:JSON 和 XML(使用框架的标准 AddJsonFile 和 AddXmlFile。XML 设置具有更高的优先级(例如,应该覆盖从JSON 文件)。
到目前为止一切正常,直到我不得不在 XML 文件中创建一个数组。有可能吗?
【问题讨论】:
标签: xml asp.net-core .net-core
参考Configuration in ASP.NET Core: Bind an array to a class
Bind支持使用配置键中的数组索引将数组绑定到对象。任何公开数字键段(:0、:1、...:{n})的数组格式都能够将数组绑定到 POCO 类数组。
我将提供两个示例来说明如何在 XML 配置文件中使用数组
使用下面的config.xml
<configuration>
<tvshow>
<metadata>
<series>Dr. Who</series>
<title>The Sun Makers</title>
<airdate>11/26/1977</airdate>
<episodes>4</episodes>
</metadata>
<actors name="0">Tom Baker</actors>
<actors name="1">Louise Jameson</actors>
<actors name="2">John Leeson</actors>
<legal>(c)1977 BBC https://www.bbc.co.uk/programmes/b006q2x0</legal>
</tvshow>
</configuration>
匹配的 POCO 看起来像
public class TvShow {
public Metadata Metadata { get; set; }
public string[] Actors { get; set; } //<-- TAKE NOTE
public string Legal { get; set; }
}
public class Metadata {
public string Series { get; set; }
public string Title { get; set; }
public DateTime AirDate { get; set; }
public int Episodes { get; set; }
}
鉴于 <actors> 元素的名称,生成的路径将是
tvshow:actors:0
tvshow:actors:1
tvshow:actors:2
遵循文档中所述的约定
所以在下面的单元测试中它会按预期通过。
[TestClass]
public class Configuration_Should {
[TestMethod]
public void Bind_Xml_Arrays() {
var config = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddXmlFile("config.xml", optional: true, reloadOnChange: true)
.Build();
var tvShow = config.GetSection("tvshow").Get<TvShow>();
tvShow.Should().NotBeNull();
tvShow.Actors.Should().HaveCount(3);
}
}
在另一个例子中,假设config.xml被修改为如下
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<tvshow>
<metadata>
<series>Dr. Who</series>
<title>The Sun Makers</title>
<airdate>11/26/1977</airdate>
<episodes>4</episodes>
</metadata>
<actors>
<names name="0">Tom Baker</names>
<names name="1">Louise Jameson</names>
<names name="2">John Leeson</names>
</actors>
<legal>(c)1977 BBC https://www.bbc.co.uk/programmes/b006q2x0</legal>
</tvshow>
</configuration>
将受影响的 POCO 重构为
public class TvShow {
public Metadata Metadata { get; set; }
public Actors Actors { get; set; }
public string Legal { get; set; }
}
//...Metadata omitted for brevity
public class Actors {
public string[] Names { get; set; }
}
同样,按照惯例,数组元素的路径需要如下所示
tvshow:actors:names:0
tvshow:actors:names:1
tvshow:actors:names:2
在测试时也符合预期
[TestClass]
public class Configuration_Should {
[TestMethod]
public void Bind_Xml_Arrays() {
var config = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddXmlFile("config.xml", optional: true, reloadOnChange: true)
.Build();
var tvShow = config.GetSection("tvshow").Get<TvShow>();
tvShow.Should().NotBeNull();
tvShow.Actors.Names.Should().HaveCount(3);
}
}
这应该与文档一起为您正确构建 XML 配置以绑定到数组提供足够的基础。
【讨论】:
您可以通过添加多个具有重复名称的数组标签来实现数组行为,包括带有元素“数组索引”的Name 属性。例如这个 json 的 xml 等价物
{
"Values": [
"value1",
"value2",
"value3"
],
"Users": [
{
"Login": "log1",
"Pass": "pass1"
},
{
"Login": "log2",
"Pass": "pass2"
}
]
}
是
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<Values name="0">value1</Values>
<Values name="1">value2</Values>
<Values name="2">value3</Values>
<Users name="0">
<Login>log1</Login>
<Pass>pas1</Pass>
</Users>
<Users name="1">
<Login>log2</Login>
<Pass>pas2</Pass>
</Users>
</configuration>
如果您检查分别解析了 xml 和 json 配置文件的 ConfigurationProviders 中的 Data
您将看到 xml 提供程序包含带有 name 后缀的额外条目,其中包含 name 属性的值,但基本上两个集合是相等的。
一些简单的测试证实了两种配置的相等性
public class Configuration
{
public List<string> Values { get; set; }
public List<User> Users { get; set; }
}
public class User
{
public string Login { get; set; }
public string Pass { get; set; }
}
//this code returns the same result for every configuration
var config = new Configuration();
_configuration.Bind(config);
string value = _configuration.GetValue<string>("Values:0");
string login = _configuration.GetValue<string>("Users:0:Login");
但这不是一个完整的答案
这种方法有一个严重的缺陷。在发布此答案之前,我测试了 xml 配置如何与第三方库一起使用。我尝试将 json 配置转换为 xml for Serilog。这个json
//..
"WriteTo": [
{ "Name": "Console" }
]
//..
转换为
//..
<WriteTo name="0">
<Name>Console</Name>
</WriteTo>
//...
此时出现异常
System.FormatException: '发现重复键 'WriteTo:0:Name'。第 11 行,位置 11。'
还记得来自 xml ConfigurationProvider 的额外条目吗?好吧,现在一切都因为他们而崩溃了。所以结论是你不能将 json 数组转换为 xml 如果它的对象包含Name 键。
一些好消息
有一个pull request 允许使用没有Name 属性的重复名称的标签。这应该可以解决这个缺点。还有 code 代表 XmlConfigurationProvider 来自您已经可以在项目中使用的同一个人。
【讨论】:
ConfigurationProvider,或者如果您迫切需要使用 xml 配置,您可以推出自己的实现。