将 XML 文件映射到 C# 类
本文关键字:映射 XML 文件 | 更新日期: 2023-09-27 18:35:26
我知道将xml反序列化为C#类是一件很常见的事情,我以前也做过。但是我收到了一个格式为 im 的 XML 文件,很难正确反序列化。当我从 XML 生成 C# 类时。我得到了一种我不想要的格式。基本上所有属性都获取列类型。
基本的 XML 格式如下所示。问题是我有 250 个属性,我真的不想手动映射每个属性。
<item table="Order">
<column columnName="Id"><![CDATA[2]]></column>
<column columnName="Price"><![CDATA[200]]></column>
<column columnName="Date"><![CDATA[25-01-2036 13:29:24:310]]>
我实际上已经手动编写了一个具有正确属性的类。 Id
、price
、date
等等...
我尝试添加ElementName
和AttributeName
但没有运气。我是否可以使用 XmlSerializer
将其直接映射到 C# 类?
它完美地映射了自动生成的类,但它最终得到了一个列和列名的列表。
有谁知道我是否可以在我的 c# 类上使用一些 xml 表示法来解决这个问题,以使其也正确映射?
--------------溶液--------------
感谢@Dan场让我走上正轨!
正如他自己指出的那样,他的解决方案中存在可为空的属性问题。所以我又追捕了一只,这就是我想出的!
public void ReadXml(XmlReader reader)
{
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element && reader.Name == "column")
{
PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(EcomOrder));
string propName = reader.GetAttribute("columnName");
// Retrieve the property Descriptor
PropertyDescriptor prop = props[propName];
if (prop != null)
{
reader.Read(); // move to CDATA (or text) node
// use DateTime.ParseExact instead for DateTime field
if (prop.PropertyType == typeof(DateTime?) || prop.PropertyType == typeof(DateTime))
{
prop.SetValue(this,
DateTime.ParseExact(reader.Value, "dd-MM-yyyy HH:mm:ss:fff", CultureInfo.InvariantCulture));
}
else
{
prop.SetValue(this,
prop.Converter.ConvertFromInvariantString(reader.Value));
}
}
else
{
throw new XmlException("Property not found: " + propName);
}
}
}
}
您可以考虑使用 XSLT 将 XML 转换为XmlSerializer
可以读取的内容,但如果只是这样,我认为这是您希望在自定义类上实现IXmlSerializable
的情况。 这是我拼凑起来的东西,使用反射将columnName
解析为 Order
类中的属性。
如果您需要序列化回此格式,则应该相当简单。
[XmlRoot("item")]
public class Order : IXmlSerializable
{
public int Id { get; set; }
public int Price { get; set; }
public DateTime Date { get; set; }
public System.Xml.Schema.XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader)
{
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element && reader.Name == "column")
{
string propName = reader.GetAttribute("columnName"); // get the property info...
PropertyInfo prop = typeof(Order).GetProperty(propName,
BindingFlags.Public | BindingFlags.Instance | BindingFlags.SetProperty);
if (prop != null && prop.CanWrite)
{
reader.Read(); // move to CDATA (or text) node
// can use Convert.ChangeType for most types
// use DateTime.ParseExact instead for DateTime field
if (prop.PropertyType != typeof(DateTime))
{
prop.SetValue(this,
Convert.ChangeType(reader.Value, prop.PropertyType, CultureInfo.InvariantCulture));
}
else
{
prop.SetValue(this,
DateTime.ParseExact(reader.Value, "dd-MM-yyyy HH:mm:ss:fff", CultureInfo.CurrentCulture));
}
}
else
{
throw new XmlException("Property not found: " + propName);
}
}
}
}
public void WriteXml(XmlWriter writer)
{
throw new NotImplementedException();
}
}
如果其中有任何其他类型需要特殊的解析注意事项,则可能需要做更多的工作。 但是,如果您获得int
节点的空值,这将引发异常 - 如果是这种情况,可能仅使用 int?
即可。 它也不包括任何类型的自动类生成 - 如果有很多属性在起作用或它们经常更改,我会编写一个实用程序来执行此操作。
我认为不可能直接映射。您可以使用XPathNavigator进行映射,也可以遵循Marc Gravell在以下文章中的建议。在 C# 中将基于属性名称的 xml 反序列化为我的类的属性
感谢@Dan场让我走上正轨!
正如他自己指出的那样,他的解决方案中存在可为空的属性问题。所以我又追捕了一只,这就是我想出的!
public void ReadXml(XmlReader reader)
{
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element && reader.Name == "column")
{
PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(EcomOrder));
string propName = reader.GetAttribute("columnName");
// Retrieve the property Descriptor
PropertyDescriptor prop = props[propName];
if (prop != null)
{
reader.Read(); // move to CDATA (or text) node
// use DateTime.ParseExact instead for DateTime field
if (prop.PropertyType == typeof(DateTime?) || prop.PropertyType == typeof(DateTime))
{
prop.SetValue(this,
DateTime.ParseExact(reader.Value, "dd-MM-yyyy HH:mm:ss:fff", CultureInfo.InvariantCulture));
}
else
{
prop.SetValue(this,
prop.Converter.ConvertFromInvariantString(reader.Value));
}
}
else
{
throw new XmlException("Property not found: " + propName);
}
}
}
}