c#通用配置文件讀寫類與格式轉換(xml,ini,json)


.NET下編寫程序的時候經常會使用到配置文件。配置文件格式通常有xmlinijson等幾種,操作不同類型配置文件需要使用不同的方法,操作較為麻煩。特別是針對同時應用不同格式配置文件的時候,很容易引起混淆。如果使用一個統一的方法對其進行操作,豈不美哉。


技術方案

思路很簡單,就是使用一個基類將配置文件的內容抽象出來,不同配置文件有不同的實現,對外統一調用方法。最開始,打算自己寫一個,后來對比ini與xml的時候(最開始沒有把json考慮進來,自己用它來做配置文件的項目較少),發現xml完全可以替代ini文件的描述,直接用xml不是更好?

於是方案就變成了用xml作為最基礎的數據對象,其他配置文件轉換成xml進行操作

XDocument VS XmlDocment

不解釋,直接看圖。

img

ini <-> xml

ini文件構造比較簡單,常見的ini有以下幾條規范:

  1. 注釋以;開頭,一直到行尾;
  2. 類別項用[]包圍,占據一整行;
  3. 類別項下可以有多個配置項,直到下一個類別項或EOF結束;
  4. 配置項格式:key=value

ini格式是二級配置結構:類別>key。通過類別和key就可以唯一獲得一個值。

public static XDocument ToXml(this string[] iniStr)
{
    //ini沒有根節點是不能直接轉成xml的。
    XDocument xdoc = new XDocument(new XElement("G" + Guid.NewGuid().ToString("N")));
    XElement node = xdoc.Root;
    foreach (var line in iniStr)
    {
        var cline = line.Trim();
        if (string.IsNullOrWhiteSpace(cline)) continue;
        switch (line[0])
        {
            case ';':
                node.Add(new XComment(cline.Substring(1)));
                break;
            case '[':
                node = new XElement(cline.Substring(1, line.Length - 2));
                xdoc.Root.Add(node);
                break;
            case '\r':
                break;
            default:
                int index = cline.IndexOf('=');
                if (index < 1)
                {
                    throw new InvalidOperationException("Property does not contains '=' operator");
                }
                node.Add(new XElement(cline.Substring(0, index).Trim(), cline.Substring(index + 1)));
                break;
        }

    }
    return xdoc;
}

ini適合比較簡單的配置讀取,文件可讀性強,讀寫也簡單;而xml具有多級結構,復雜,還有dtd,xslt什么的,相對來說比較全面。因此,從xml轉成ini,要求xml符合ini二級結構,詳見
源代碼中的IniExtensions類。

xml <-> json

json在前端不要太火,用途廣泛,占用空間小,機器也較好識別。但是由於最開始沒考慮到json,就先上了xml的船,也懶得去想是不是json作為基礎結構更好了,先轉換json到xml吧。
json與xml互轉:直接用Newtonsoft.Json就可以了。使用SerializeXNodeDeserializeXNode就可以完成轉換。

注意,xml與json互轉,有一些地方需要小心。

  • xml的屬性,轉成json會加上前綴“@”
  • xml如果帶聲明的話,轉成json就有前綴“?”
  • xml同級相同名稱的元素在json中會構成一個數組
  • xml的某一級只有一個元素時,如果需要轉成數組,需要加上json:Array='true'屬性

考慮轉換都在內部,讀取json->xml->保存json。這個流程不需要考慮以上問題。

//json沒有根節點是不能直接轉成xml的。
private static XDocument DecorateJson(string jsonStr)
{
    return JsonConvert.DeserializeXNode(jsonStr, "G" + Guid.NewGuid().ToString("N"));
}

封裝問題

xml要求必須有且只有一個根節點,這一點,ini不滿足,json有的不滿足,因此需要添加一個默認根節點來處理這個問題。

為了防止配置項目和節點名稱沖突,使用Guid.NewGuid()來得到不重復的值,由於xml要求元素名稱不能以數字開頭,所以人為添加一個“G”前綴,感覺不是很優雅,有辦法還請告知~

操作配置文件

不管文件類型是什么,都用代表一個對象代表一個配置文件。由於使用了XDocment,那么使用linq是比較直接的選擇。但是考慮到讀取配置的時候,一般用戶都清楚需要讀取的項目,使用查詢反而不是很直觀。

這里我采用XPathXPath是一門在 XML 文檔中查找信息的語言。XPath 可用來在 XML 文檔中對元素和屬性進行遍歷。相關資料可以參見http://www.w3school.com.cn/xpath

XPath的用法很簡單,對簡單的配置項就和文件路徑一樣。這里我使用索引器來進行數據讀取配置,也提供了GetValue和SetValue方法。

public void Test1()
{
    string str = @";時間單位為ms
[Dynamic]
Interval = 5
Delay = 4000

[Default]
Interval = 5
";
    System.IO.File.WriteAllText("test.ini", str);
    // e.g. "//config/general/interval" 從任意節點開始,檢索config節點下general節點下的interval節點
    //     "/config/tick[@type='origin']" 從根節點檢索config節點下,屬性type='origin'的tick節點(只對XML)
    ConfigManager config = new ConfigManager("test.ini");
    Assert.Equal("5", config[@"//Default/Interval"]);
    Assert.Equal("5", config.GetValue("Default", "Interval"));
}

對以上配置,可以直接使用"//Delay"獲得Delay的值(沒有重名),對層級較深的配置,用戶不需要關心其他細節,直切正題,這么用還是很爽快的~

完整代碼

完整代碼在github上,可以支持.net framework/.net core/xamarin。

https://github.com/circler3/UnifiedConfig

對應有nuget包,搜索nuget或者使用

Install-Package UnifiedConfig

即可將引用添加到項目。

特性

  • 配置文件統一操作接口(讀取,修改,保存)
  • 可拓展配置類型支持
  • 自動推斷文件類型


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM