【问题标题】:Arrays in ASP.NET Core's XML Configuration ProviderASP.NET Core 的 XML 配置提供程序中的数组
【发布时间】:2019-02-27 22:29:19
【问题描述】:

我正在编写的 ASP.NET Core (2.1) 应用程序使用两个配置源:JSON 和 XML(使用框架的标准 AddJsonFileAddXmlFile。XML 设置具有更高的优先级(例如,应该覆盖从JSON 文件)。

到目前为止一切正常,直到我不得不在 XML 文件中创建一个数组。有可能吗?

【问题讨论】:

    标签: xml asp.net-core .net-core


    【解决方案1】:

    参考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; }
    }
    

    鉴于 &lt;actors&gt; 元素的名称,生成的路径将是

    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 配置以绑定到数组提供足够的基础。

    【讨论】:

      【解决方案2】:

      您可以通过添加多个具有重复名称的数组标签来实现数组行为,包括带有元素“数组索引”的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>
      

      如果您检查分别解析了 xmljson 配置文件的 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 来自您已经可以在项目中使用的同一个人。

      【讨论】:

      • @MichałDudak 好吧,这真的很不幸。正如我在回答中提到的,考虑使用来自 github 的自定义 ConfigurationProvider,或者如果您迫切需要使用 xml 配置,您可以推出自己的实现。
      猜你喜欢
      • 2019-06-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多