将 XML 文件映射到 C# class
Mapping XML file to a C# class
我知道将 xml 反序列化为 C# classes 是很常见的事情,我以前也做过。但是我收到了一个 XML 格式的文件,我很难正确反序列化。当我从 XML 生成 C# class 时。我得到一种我不想要的格式。基本上所有属性都获得列类型..
基本的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]]>
我实际上已经手动编写了一个具有正确属性的 class。 Id
、price
、date
等等...
我试过添加 ElementName
和 AttributeName
但没有成功。我可以使用 XmlSerializer
将其直接映射到 C# class 吗?
它完美地映射了自动生成的 class 但它以列和 columnName 的列表结束..
有谁知道我是否可以在我的 c# class 上使用一些 xml 符号来解决这个问题,使其也能正确映射?
----------------解决方案------------
感谢@Dan Field 让我走上正轨!
正如他自己指出的那样,他的解决方案中存在可为空属性的问题。所以我又找到了一个,这就是我想出的!
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);
}
}
}
}
我认为不能直接映射。
您可以使用 XPathNavigator 进行映射,也可以遵循 Marc Gravell in the following post 的建议
Deserialize xml base on attribute name as properties of my class in C#
您可能会考虑使用 XSLT 将 XML 转换为 XmlSerializer
可以阅读的内容,但如果仅此而已,我认为在这种情况下您需要实施 IXmlSerializable
自定义 class。这是我拼凑的东西,使用反射将 columnName
解析为 Order
class.
中的 属性
如果您需要序列化回这种格式,这应该是相当简单的。
[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?
即可。它还不包括任何类型的自动生成 class - 如果有很多属性在起作用或它们经常更改,我会编写一个实用程序来执行此操作。
感谢@Dan Field 让我走上正轨!
正如他自己指出的那样,他的解决方案中存在可为空属性的问题。所以我又找到了一个,这就是我想出的!
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);
}
}
}
}
我知道将 xml 反序列化为 C# classes 是很常见的事情,我以前也做过。但是我收到了一个 XML 格式的文件,我很难正确反序列化。当我从 XML 生成 C# class 时。我得到一种我不想要的格式。基本上所有属性都获得列类型..
基本的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]]>
我实际上已经手动编写了一个具有正确属性的 class。 Id
、price
、date
等等...
我试过添加 ElementName
和 AttributeName
但没有成功。我可以使用 XmlSerializer
将其直接映射到 C# class 吗?
它完美地映射了自动生成的 class 但它以列和 columnName 的列表结束..
有谁知道我是否可以在我的 c# class 上使用一些 xml 符号来解决这个问题,使其也能正确映射?
----------------解决方案------------
感谢@Dan Field 让我走上正轨!
正如他自己指出的那样,他的解决方案中存在可为空属性的问题。所以我又找到了一个,这就是我想出的!
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);
}
}
}
}
我认为不能直接映射。 您可以使用 XPathNavigator 进行映射,也可以遵循 Marc Gravell in the following post 的建议 Deserialize xml base on attribute name as properties of my class in C#
您可能会考虑使用 XSLT 将 XML 转换为 XmlSerializer
可以阅读的内容,但如果仅此而已,我认为在这种情况下您需要实施 IXmlSerializable
自定义 class。这是我拼凑的东西,使用反射将 columnName
解析为 Order
class.
如果您需要序列化回这种格式,这应该是相当简单的。
[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?
即可。它还不包括任何类型的自动生成 class - 如果有很多属性在起作用或它们经常更改,我会编写一个实用程序来执行此操作。
感谢@Dan Field 让我走上正轨!
正如他自己指出的那样,他的解决方案中存在可为空属性的问题。所以我又找到了一个,这就是我想出的!
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);
}
}
}
}