[英]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
因为编译器“知道”有对象模型中没有这样的属性,因此您收到一个编译时错误,而不是在运行时应用程序崩溃。
因此,要回答最重要的问题,一旦创建了正确的对象模型,就可以使用.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();
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.