我正在编写一些代码来解析以下格式的xml文件(为简单起见而截断)
<?xml version="1.0" encoding="UTF-8"?>
<ship name="Foo">
<base_type>Foo</base_type>
<GFX>fooGFX</GFX>
....
</ship>
我正在使用一个由参数和xpath值键组成的字典,并查询它以将所有值加载到各种变量中。但是,我注意到会有大量的代码重复。实际上,对于我想要检索的每个值,我将不得不编写另一行几乎相同的代码。这是我的代码:
class Ship
{
public Ship()
{
paths = new Dictionary<string, string>();
doc = new XmlDocument();
//Define path to various elements
paths.Add("name", "/ship/@name");
paths.Add("base_type", "/ship/base_type");
paths.Add("GFX", "/ship/GFX");
}
public void LoadFile(string filename)
{// Loads the file and grabs the parameters
doc.Load(filename);
Name = doc.SelectSingleNode(paths["name"]).Value;
Base_type = doc.SelectSingleNode(paths["base_type"]).Value;
GFX = doc.SelectSingleNode(paths["GFX"]).Value;
}
public Dictionary<string, string> paths; //The XPaths to the various elements, define them in constructor
public XmlDocument doc;
public string Name;
public string Base_type;
public string GFX;
}
请注意这里的重复:
variable = doc.SelectSingleNode(paths["variable_name"]).value.
还会有更多的变量,所以这一部分将是巨大的。
有没有简化这个?如果这是C ++,我可能会尝试指针,但我知道它们不推荐用于C#,所以有类似的方法吗?
我会寻找一些我可以提供变量名称和xpath列表的东西,并让代码拉出所有值并将它们加载到变量中的某种循环或其他东西。我想使用XPath,因为我希望这个文件的格式可能会定期更改。
有什么想法吗?
提前致谢。
编辑:我也希望能够修改这些数据并将其保存回来。如果有必要,我不会保存整个新树,但如果可能的话,修改数据会很好。我不需要修改的解决方案,但我只需要打开这个选项。
答案 0 :(得分:2)
实现对象填充的一个好方法是使用XmlSerializer.Deserialize()。
这样的事情:
namespace TestSerialization
{
using System;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
public class TestSerialization
{
static void Main(string[] args)
{
string theXml =
@"<ship name='Foo'>
<base_type>Foo</base_type>
<GFX>fooGFX</GFX>
</ship>";
Ship s = Ship.Create(theXml);
// Write out the properties of the object.
Console.Write(s.Name + "\t" + s.GFX);
}
}
[XmlRoot("ship")]
public class Ship
{
public Ship() { }
public static Ship Create(string xmlText)
{
// Create an instance of the XmlSerializer specifying type.
XmlSerializer serializer = new XmlSerializer(typeof(Ship));
StringReader sr = new StringReader(xmlText);
XmlReader xreader = new XmlTextReader(sr);
// Use the Deserialize method to restore the object's state.
return (Ship)serializer.Deserialize(xreader);
}
[XmlAttribute("name")]
public string Name;
[XmlElement("base_type")]
public string Base_type;
public string GFX;
}
}
更新:OP增加了一个问题:
我还希望能够修改此数据并将其保存回来。一世 如果有必要,我不会保存整个新树
只需使用XmlSerializer.Serialize()
方法即可。
以下是使用它的典型示例:
// Create an XmlTextWriter using a FileStream.
Stream fs = new FileStream(filename, FileMode.Create);
XmlWriter writer =
new XmlTextWriter(fs, Encoding.Unicode);
// Serialize using the XmlTextWriter.
serializer.Serialize(writer, yourObject);
writer.Close();
答案 1 :(得分:0)
这对您来说可能听起来很奇怪,但如果您要获取大量数据,那么就个人而言,我会选择自动生成的代码解决方案。
含义:映射所有XML名称 - &gt;他们的XPath表达式 - &gt;显示名称,并为您自动生成数据类。
例如:
假设输入文件是CSV格式(或制表符分隔符等):
<强> DisplayName的,XMLField,XPathExpr 强>
名称,名称/船/ @名称
Base_type,base_type,/船/ base_type
GFX,GFX,/船舶/ GFX
现在你需要一些能自动生成代码的进程。为此,您可以开发一个C#实用程序,使用一些脚本语言,如Perl,甚至可以创建一些XSL转换。
最终结果如下:
class AutoGeneratedShipData
{
public AutoGeneratedShipData(XmlDocument xmlDoc)
{
// Code initialization like in your sample
}
public string Name ...
public string Base_type ...
public string GFX ...
}
您可以继续并添加序列化,INotifyPropertyChanged支持以及您可能认为合适的其他装饰。
另一种方法
使用反射来将数据加载到属性中,但为此需要手动为每个数据成员创建属性以及数据映射。
以下是一个例子:
LoadData(xmlDoc, "Name", "/ship/@name");
LoadData(xmlDoc, "Base_type", "/ship/base_type");
LoadData(xmlDoc, "GFX", "/ship/GFX");
LoadData()
的位置如下:
private void LoadData(XmlDocument xmlDoc, Dictionary<string, string> propertyNameToXPathMap)
{
foreach ( PropertyInfo pi in this.GetType().GetProperties() )
{
// Is the property mapped to an xpath?
if ( propertyNameToXPathMap.ContainsKey(pi.Name) )
{
string sPathExpression = propertyNameToXPathMap[pi.Name];
// Extract the Property's value from XML based on the xpath expr.
string value = xmlDoc.SelectSingleNode(sPathExpression).Value;
// Set this object's property's value
pi.SetValue(this, value, null);
}
}
}
paths
词典,因为我没有看到任何特殊的角色。答案 2 :(得分:0)
一种方法是定义自己的custom attribute type,用于将属性映射到XPath选择器,为需要映射到XPath选择器的变量定义自动属性,并使用自定义属性修饰这些属性。例如:
[MapTo("/ship/@name")]
public string Name { get; set; }
[MapTo("/ship/base_type")]
public string BaseType { get; set; }
然后在加载XML文档之后,编写一个循环,该循环使用反射来遍历每个属性,并根据其关联的XPath选择器设置它们的值。例如,假设以这种方式声明自定义属性:
[AttributeUsage(AttributeTargets.Property)]
public class MapToAttribute : Attribute
{
public string XPathSelector { get; private set; }
public MapToAttribute(string xPathSelector)
{
XPathSelector = xPathSelector;
}
}
然后执行映射的循环,假设它位于保存映射属性的类中的实例方法内(如果没有,将this
替换为目标对象),就像这样:
foreach (var property in this.GetType().GetProperties())
{
var mapToAttribute = property.GetCustomAttributes(typeof(MapToAttribute), false).SingleOrDefault() as MapToAttribute;
if (mapToAttribute != null)
{
property.SetValue(this, doc.SelectSingleNode(mapToAttribute.XPathSelector).Value);
}
}