天天看點

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

.NET下編寫程式的時候經常會使用到配置檔案。配置檔案格式通常有

xml

ini

json

等幾種,操作不同類型配置檔案需要使用不同的方法,操作較為麻煩。特别是針對同時應用不同格式配置檔案的時候,很容易引起混淆。如果使用一個統一的方法對其進行操作,豈不美哉。

技術方案

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

于是方案就變成了用xml作為最基礎的資料對象,其他配置檔案轉換成xml進行操作。

XDocument VS XmlDocment

不解釋,直接看圖。

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

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

就可以了。使用SerializeXNode和DeserializeXNode就可以完成轉換。

注意,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是比較直接的選擇。但是考慮到讀取配置的時候,一般使用者都清楚需要讀取的項目,使用查詢反而不是很直覺。

這裡我采用XPath。XPath是一門在 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
           

即可将引用添加到項目。

特性

  • 配置檔案統一操作接口(讀取,修改,儲存)
  • 可拓展配置類型支援
  • 自動推斷檔案類型

除非特殊說明,本作品由podolski創作,采用知識共享署名 4.0 國際許可協定進行許可。歡迎轉載,轉載請保留原文連結~喜歡的觀衆老爺們可以點下關注或者推薦~