.NET Core採用的全新配置系統[9]: 為什麼針對XML的支持不夠好?如何改進?

来源:http://www.cnblogs.com/artech/archive/2016/08/15/new-config-system-09.html
-Advertisement-
Play Games

物理文件是我們最常用到的原始配置的載體,最佳的配置文件格式主要由三種,它們分別是JSON、XML和INI,對應的配置源類型分別是JsonConfigurationSource、XmlConfigurationSource和IniConfigurationSource。但是對於.NET Core的配置... ...


物理文件是我們最常用到的原始配置的載體,最佳的配置文件格式主要由三種,它們分別是JSON、XML和INI,對應的配置源類型分別是JsonConfigurationSource、XmlConfigurationSource和IniConfigurationSource。但是對於.NET Core的配置系統來說,我們習以為常的XML反倒不是理想的配置源,至少和JSON比較起來,它具有一個先天不足的劣勢,那就是針對集合數據結構的支持不如人意。[ 本文已經同步到《ASP.NET Core框架揭秘》之中]

一、為什麼針對集合的配置難以通過優雅的XML來表示

在《配置模型設計詳解》一文中我們對配置模型的設計和實現進行了詳細介紹。在此文中我們說應用中的配置體現為一種樹形化的層次結構,所我將它稱為“配置樹”,具體的配置數據通過配置樹的“葉子節點”承載。當配置數據從不同的來源載入之後都會轉換成一個字典,我將其稱為“配置字典”。為了讓“配置字典”能夠存儲“配置樹”的所有數據和自身結構,我們需要在配置字典中存儲所有葉子節點,葉子節點的路徑和值將直接作為字典元素的Key和Value。由於字典的Key是唯一的,這就要求配置樹中的每一個節點必須具有唯一的路徑。XmlConfigurationSource/XmlConfigurationProvider不能很好地支持集合數據結構的問題就出現在這裡。

   1: public class Profile
   2: {
   3:     public Gender         Gender { get; set; }
   4:     public int            Age { get; set; }
   5:     public ContactInfo    ContactInfo { get; set; }
   6: }
   7:  
   8: public class ContactInfo
   9: {
  10:     public string EmailAddress { get; set; }
  11:     public string PhoneNo { get; set; }
  12: }
  13:  
  14: public enum Gender
  15: {
  16:     Male,
  17:     Female
  18: }

舉個簡單的例子,假設需要採用XML來表示一個Profile對象的集合(Profile的類型具有如上所示的定義),那麼我們很自然地會採用如下的結構。

   1: <Profiles>
   2:   <Profile Gender="Male" Age="18">
   3:     <ContactInfo EmailAddress ="[email protected]" PhoneNo="123"/>
   4:   </Profile>
   5:   <Profile Gender="Male" Age="25">
   6:     <ContactInfo EmailAddress ="[email protected]" PhoneNo="456"/>
   7:   </Profile>
   8:   <Profile Gender="Male" Age="40">
   9:     <ContactInfo EmailAddress ="[email protected]" PhoneNo="789"/>
  10: </Profile>

對於這段XML結構,XmlConfigurationProvider會採用“簡單粗暴”的方式將它映射為如下所示的“配置樹”。由於這棵樹直接將XML元素的名稱作為配置節點名稱,所以三個Profile對象在這棵樹中的根節點都以“Profile”命名,毫無疑問,這顆樹將不能使用字典來表示,因為它不能保證所有的節點都具有不同的路徑

image

二、按照配置樹的要求對XML結構稍作轉換

之所以XML不能像JSON格式那樣可以以一種很自然的形式表示集合或者數組,是因為後者對這兩種數據類型提供了明確的定義方式(採用中括弧定義),但是XML只有子元素的概念,我們不能確定它的子元素是否是一個集合。如果做這樣一個假設:如果同一個XML元素下的所有子元素都具有相同的名稱,那麼我們可以將其視為集合。根據這麼一個假設,我們對XmlConfigurationSource略加改造就可以解決XML難以表示集合數據結構的問題。

我們通過派生XmlConfigurationSource創建一個新的ConfigurationSource類型,姑且將其命名為ExtendedXmlConfigurationSource。XmlConfigurationSource提供的ConfigurationProvdier類型為ExtendedXmlConfigurationProvider,它派生於XmlConfigurationProvider。在重寫的Load方法中,ExtendedXmlConfigurationProvider通過對原始的XML結構進行相應的改動,從而讓原本不合法的XML(XML元素具有相同的名稱)可以轉換成一個針對集合的配置字典 。下圖展示了XML結構轉換採用的規則和步驟。

12

如上圖所示,針對集合對原始XML所作的結構轉換由兩個步驟組成。第一步為表示集合元素的XML元素添加一個名為“append_index”的屬性(Attribute),我們採用零基索引作為該屬性的值。第二步會根據第一步轉換的結果創建一個新的XML,同名的集合元素(比如<profile>)將會根據添加的索引值從新命名(比如<profile_index_0>)。毫無疑問,轉換後的這個XML可以很好地表示一個集合對象。如下所示的是ExtendedXmlConfigurationProvider的定義,上述的這個轉換邏輯體現在重寫的Load方法中。

   1: public class ExtendedXmlConfigurationProvider : XmlConfigurationProvider
   2: {
   3:    public ExtendedXmlConfigurationProvider(XmlConfigurationSource source) : base(source)
   4:     {}
   5:  
   6:     public override void Load(Stream stream)
   7:     {
   8:         //載入源文件並創建一個XmlDocument        
   9:         XmlDocument sourceDoc = new XmlDocument();
  10:         sourceDoc.Load(stream);
  11:  
  12:         //添加索引
  13:         this.AddIndexes(sourceDoc.DocumentElement);
  14:  
  15:         //根據添加的索引創建一個新的XmlDocument
  16:         XmlDocument newDoc = new XmlDocument();
  17:         XmlElement documentElement = newDoc.CreateElement(sourceDoc.DocumentElement.Name);
  18:         newDoc.AppendChild(documentElement);
  19:  
  20:         foreach (XmlElement element in sourceDoc.DocumentElement.ChildNodes)
  21:         {
  22:             this.Rebuild(element, documentElement, 
  23:                 name => newDoc.CreateElement(name));
  24:         }
  25:  
  26:         //根據新的XmlDocument初始化配置字典
  27:         using (Stream newStream = new MemoryStream())
  28:         {
  29:             using (XmlWriter writer = XmlWriter.Create(newStream))
  30:             {
  31:                 newDoc.WriteTo(writer);
  32:             }
  33:             newStream.Position = 0;
  34:             base.Load(newStream);
  35:         }
  36:     }
  37:  
  38:     private void AddIndexes(XmlElement element)
  39:     {
  40:         if (element.ChildNodes.OfType<XmlElement>().Count() > 1)
  41:         {
  42:             if (element.ChildNodes.OfType<XmlElement>().GroupBy(it => it.Name).Count() == 1)
  43:             {
  44:                 int index = 0;
  45:                 foreach (XmlElement subElement in element.ChildNodes)
  46:                 {
  47:                     subElement.SetAttribute("append_index", (index++).ToString());
  48:                     AddIndexes(subElement);
  49:                 }
  50:             }
  51:         }
  52:     }
  53:  
  54:     private void Rebuild(XmlElement source, XmlElement destParent, Func<string, XmlElement> creator)
  55:     {
  56:         string index = source.GetAttribute("append_index");
  57:         string elementName = string.IsNullOrEmpty(index) ? source.Name : $"{source.Name}_index_{index}";
  58:         XmlElement element = creator(elementName);
  59:         destParent.AppendChild(element);
  60:         foreach (XmlAttribute attribute in source.Attributes)
  61:         {
  62:             if (attribute.Name != "append_index")
  63:             {
  64:                 element.SetAttribute(attribute.Name, attribute.Value);
  65:             }
  66:         }
  67:  
  68:         foreach (XmlElement subElement in source.ChildNodes)
  69:         {
  70:             Rebuild(subElement, element, creator);
  71:         }
  72:     }
  73: }

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 比如 ssh 10.0.1.23,出現以下情況: @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @@@@@@@@@@@@@@@ ...
  • 版權聲明:本文為MULTIBEANS ORG研發跟隨文章,未經MLT ORG允許不得轉載。 最近做項目,需要開發安卓應用,實現串口的收發,目測CH340G在安卓手機上非常麻煩,而且驅動都是Java版本的, 就沒選擇,博主在大二的時候學習過Java SE基本的語法,寫過一些小程式就放棄了Java的道路 ...
  • 最近在做項目中用到2013中的ReportView11 在本機IIS中使用完全沒問題 但是放到伺服器上總是出問題 解決辦法:(1)首先在自己機器上開發的時候 是不用引用 Microsoft.ReportViewer.Common.dll和Microsoft.ReportViewer.WebForms ...
  • C# 軟體配置方法。通常情況下我們使用appSetting來進行配置,通過 Glacier 可以使用嵌入的 HOCON 或獨立的 HOCON 配置文件配置,同時支持類型綁定功能,方便統一管理軟體配置。 ...
  • 最近閑來無事給自己寫了家庭財務收支管理系統,也就包含支出管理,收入管理和一些統計功能。 先說登錄模塊,因為涉及GET和POST請求,這些東西都是能被監控和抓取的所以就考慮這使用RSA加密解密方式傳輸用戶名和密碼參數,頁面JS如下: 1 /*需要引入三個JS文件,BigInt.js、RSA.js和Ba ...
  • 靜態 1、普通成員普通成員都是屬於對象的用對象調用 2、靜態成員靜態成員是屬於類的用類名調用 class FenBi{public int length;//普通成員public string color;//} static 靜態關鍵字 靜態方法裡面不能包含普通成員 普通方法裡面可以包含靜態成員 ...
  • 我們先思考幾個問題: 接下來,先開始我們的正文。 自己實現迭代器 .net中迭代器是通過IEnumerable和IEnumerator介面來實現的,今天我們也來依葫蘆畫瓢。 首先來看看這兩個介面的定義: 並沒有想象的那麼複雜。其中IEnumerable只有一個返回IEnumerator的GetEnu ...
  • 2016.6.27 微軟已經正式發佈了.NET Core 1.0 RTM,但是工具鏈還是預覽版,同樣的大量的開源測試庫也都是至少發佈了Alpha測試版支持.NET Core, 這篇文章 The State of .Net Core Testing Today 就將各個開源測試庫的目前進展進行了彙總。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...