繁体   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