簡體   English   中英

XML到列表轉換器? XML是否是正確的工具?

[英]XML to List converter? Is XML the right tool anyway?

我從PHP到C#,所以請原諒我的一些術語。

當前,我正在一個需要將多個配置文件存儲在一個文件中的小型項目,所以我決定使用XML,因為C#確實不支持INI文件(通常是我處理基於文本的東西)。 至少不能令人滿意。

這是我的XML文件的基本結構:

<profile name="FooBar">
    <btnA pressed="actionA" released="actionB" />
    <btnB pressed="actionX" released="actionY" />
    ...
</profile>
<profile name="XYZ">
    <btnA pressed="actionA" released="actionB" />
    <btnB pressed="actionX" released="actionY" />
    ...
</profile>

在PHP中,我將生成具有以下結構的關聯數組:

<?php
  foo = array(
    'FooBar' => array(
      'btnA_pressed' => 'actionA',
      'btnA_released' => 'actionB'
      // ...
    ),
    'XYZ' => array(
      // ...
    )
  );

編輯開始

應用程序類結構:

  • 設置(包含所有配置文件和對當前配置文件的引用)
  • 個人資料(見下文)

Profile類:

public class Profile 
{
    private string _name;
    public string Name 
    {
        get { return this._name; }
        set { this._name = value;}
    }

    private string _btnA_pressed;
    public string BtnA_Pressed { get; set; }
    // and so on...

    public Profile(var data) { // arg is a placeholder
        // associate the data with the properties
    }
}

簡而言之,Settings類包含所有概要文件和對所選概要文件的引用。 對配置文件的訪問將通過Settings.CurrentProfile.propertie_Name()

編輯結束

現在的問題是,我如何在C#中實現相同或相似的目的? 還是有更好的方法來實現同一目標?

謝謝您的幫助!

可以使用LINQ2XML輕松地處理XML結構,而無需鍵入模型(類)。

讀取包含許多profile節點的XML文件(我假設您的XML文件是正確的,並且具有一個根節點),看起來可能像這樣:

// read existing XML structure
var xml = XDocument.Load("d:\\temp\\xml\\profile.xml");
// take all profile elements
var profiles = xml.Root.Elements("profile").ToList();
foreach (var profile in profiles)
{
    Console.WriteLine(profile.Attribute("name").Value);
    // find all button elements
    var buttons = profile
                    .Elements()
                    .Where (e => e.Name.ToString().StartsWith("btn"));
    // list elements
    foreach (var button in buttons)
    {
        // tag name
        var name = button.Name.ToString();
        // attributes
        var pressed = button.Attribute("pressed").Value;
        var released = button.Attribute("released").Value;
        Console.WriteLine(String.Format("{0} - P'{1}' - R'{2}'", name, pressed, released));
    }
}

輸出為:

FooBar
btnA - P'actionA' - R'actionB'
btnB - P'actionX' - R'actionY'
XYZ
btnA - P'actionA' - R'actionB'
btnB - P'actionX' - R'actionY'

string讀取一個配置文件XML結構,然后創建一個新的XML結構可能看起來像這樣:

var xmlCode = @"<profile name=""FooBar""><btnA pressed=""actionA"" released=""actionB"" /><btnB pressed=""actionX"" released=""actionY"" /></profile>";

try
{
    // read existing XML structure
    var xml = XDocument.Parse(xmlCode); // XDocument.Load("c:\\path\\to\\file.xml");
    // find all button elements
    var buttons = xml.Root
                     .Elements()
                     .Where (e => e.Name.ToString().StartsWith("btn"));
    // list elements
    foreach (var button in buttons)
    {
        // tag name
        var name = button.Name.ToString();
        // attributes
        var pressed = button.Attribute("pressed").Value;
        var released = button.Attribute("released").Value;
        Console.WriteLine(String.Format("{0} - P'{1}' - R'{2}'", name, pressed, released));
    }
    // create xml
    // root element
    var newXml = new XElement("profile", new XAttribute("name", "FooBaz"), 
                    new XElement("btnA", 
                        new XAttribute("pressed", "actionX"), 
                        new XAttribute("released", "actionXR")),
                    new XElement("btnB", 
                        new XAttribute("pressed", "actionZ"), 
                        new XAttribute("released", "actionZR")));
    Console.WriteLine(newXml.ToString());
}
catch (Exception exception)
{
    Console.WriteLine(exception.Message);
}

輸出為:

btnA - P'actionA' - R'actionB'
btnB - P'actionX' - R'actionY'
<profile name="FooBaz">
    <btnA pressed="actionX" released="actionXR" />
    <btnB pressed="actionZ" released="actionZR" />
</profile>

您可以使用LINQ2XML讀取數據並填寫Profile類型的對象列表,如下所示:

// read existing XML structure
var xml = XDocument.Load("d:\\temp\\xml\\profile.xml");
// take all profile elements
var profiles = xml.Root.Elements("profile").ToList();
var listOfProfiles = new List<Profile>();
foreach (var profile in profiles)
{
    var profileObject = new Profile("");
    profileObject.Name = profile.Attribute("name").Value;
    // find all button elements
    var buttons = profile
                    .Elements()
                    .Where (e => e.Name.ToString().StartsWith("btn"));
    // list elements
    foreach (var button in buttons)
    {
        // attributes
        var pressed = button.Attribute("pressed").Value;
        var released = button.Attribute("released").Value;
        profileObject.BtnA_Pressed = pressed;
    }
    listOfProfiles.Add(profileObject);
}

您還可以使用XML序列化 -您需要將XML結構描述為一個類(類型化模型),然后反序列化 (將XML文件讀入您​​的類)。 序列化 (將XML結構寫入文件)。 序列化方法的一般實現。 反序列化看起來像這樣:

public void SerializeModel<T>(string fqfn, T entity)
{
    var xmls = new XmlSerializer(entity.GetType());
    var writer = new StreamWriter(fqfn);
    xmls.Serialize(writer, entity);
    writer.Close();
}

public T DeserializeModel<T>(string fqfn)
{
    var fs = new FileStream(fqfn, FileMode.Open);
    var xmls = new XmlSerializer(typeof(T));
    var r = (T) xmls.Deserialize(fs);
    fs.Close();
    return r;
}

描述您的Profile類和其中包含的列表的類型化模型如下所示(請注意不同XML序列化屬性的用法):

public class Profiles
{
    [XmlElement(ElementName="Profile")]
    public List<Profile> Profile { get; set; }
}

public class Profile
{
    [XmlArray(ElementName="Buttons")]
    public List<Button> Buttons { get; set; }
    [XmlAttribute]
    public String Name;
}

public class Button
{
    [XmlAttribute]
    public String Pressed { get; set; }
    [XmlAttribute]
    public String Released;
}

創建XML文件:

    var profiles = new Profiles();
    var profileA = new Profile();
    var profileB = new Profile();
    var buttonA = new Button();
    var buttonB = new Button();

    profileA.Buttons = new List<Button>();
    profileB.Buttons = new List<Button>();
    profiles.Profile = new List<Profile>();

    profileA.Name = "Profile A";
    profileB.Name = "Profile B";
    buttonA.Pressed = "Pressed A";
    buttonA.Released = "Release A";
    buttonB.Pressed = "Pressed B";
    buttonB.Released = "Release B";

    profileA.Buttons.Add(buttonA);
    profileB.Buttons.Add(buttonB);
    profiles.Profile.Add(profileA);
    profiles.Profile.Add(profileB);

    var xmlFile = "d:\\temp\\xml\\profile_model.xml";
    SerializeModel<Profiles>(xmlFile, profiles);

新的XML文件如下所示(請注意,由於.NET中XML序列化處理數組/列表的方式,結構進行了少許修改):

<?xml version="1.0" encoding="utf-8"?>
<Profiles xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Profile Name="Profile A">
    <Buttons>
      <Button Released="Release A" Pressed="Pressed A" />
    </Buttons>
  </Profile>
  <Profile Name="Profile B">
    <Buttons>
      <Button Released="Release B" Pressed="Pressed B" />
    </Buttons>
  </Profile>
</Profiles>

然后可以這樣讀取文件:

    var input = DeserializeModel<Profiles>(xmlFile);
    foreach (var profile in input.Profile)
    {
        var b = profile.Buttons.First();
        Console.WriteLine(String.Format("{0} - {1} - {2}", profile.Name, b.Pressed, b.Released));
    }

輸出是預期的:

Profile A - Pressed A - Release A
Profile B - Pressed B - Release B

兩種方法都有優點和缺點。

IMO對您問題的答案(有所更改) Is XML the correct approach for saving structured data to a file? 是-絕對可以! 如今,XML是用於表示/操縱/交換結構化數據和一般數據(數據保存在字符串中)的標准之一。 正如已經提到的,INI文件並不是要真正代表復雜的嵌套結構。

與可能習慣於處理大量數組和基於魔術字符串的東西的PHP相比,C#是一種靜態類型的語言,通常建議您創建一個正確的,形式上定義的,結構化的數據模型 ,以使您能夠操縱數據您正在以強類型處理。

例如,這意味着,如果您要處理與個人信息有關的數據,並且需要處理姓氏名字年齡的概念,則需要創建一個包含以下形式的類的類的類的Properties ,例如:

public class Person
{
    public string LastName {get;set;}

    public string FirstName {get;set;}

    public int Age {get;set;}

    //And so on...
}

請注意,每個屬性如何具有適當的數據類型 ,該數據類型使您可以限制其可以包含的值。 例如, Age屬性自動是int類型(整數)的事實意味着您永遠不能在其中包含諸如"abc"之類的代碼,並且代碼如下:

var person = new Person();
person.Age = "abc";

還會產生編譯時錯誤,而不是在運行時崩潰,或者在存儲的數據中產生任何類型的不一致。

同樣,如果您的Person對象(在您嘗試建模的現實世界數據中)與某個地址有關,例如Address ,那么您還將創建Address類:

public class Address
{
    public string Line1 {get;set;} 

    public string Line2 {get;set;}

    public string City {get;set;}

    //And so on...
}

然后通過在Person類中創建一個附加屬性來對此Person -> Address關系建模:

public class Person
{
   //.. All of the above.

   public Address Address {get;set;}
}

可以用這樣的圖來說明:

在此處輸入圖片說明

此方法具有以下優點:

  • 它提供了編譯時檢查,以檢查代碼的正確性。 可以在開發周期的早期就捕獲(並且需要更正)編譯時錯誤。 例如,您不能執行以下操作:

person.LatsName在屬性名LastName的拼寫錯誤LatsName因為編譯器“知道”有對象模型中沒有這樣的屬性,因此您收到一個編譯時錯誤,而不是在運行時應用程序崩潰。

  • 它為諸如AutoComplete之類的功能提供了IDE支持,因為IDE“知道”您正在處理的對象模型,因此它知道每個類具有哪些屬性/方法/事件(成員),以及預期的參數和數據類型是什么每一個。

因此,要回答最重要的問題,一旦創建了正確的對象模型,就可以使用.Net內置的序列化/反序列化機制將對象模型中的類實例(具有實際值和屬性)轉換為可以被存儲在磁盤上的文件中,例如XML。

假設您有一個包含所有相關字段/屬性的profile類,則可以

    Profile profile = new Profile();
    profile.actionA = "actionA"; // or whatever you have


    var file = new System.IO.StreamWriter(@"c:\temp\Profile_as_xml.xml");

    var writer = new System.Xml.Serialization.XmlSerializer(typeof(Profile));
    writer.Serialize(file, profile);

    file.Close();

請參閱http://msdn.microsoft.com/en-us/library/ms172873.aspx

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM