给出一个XElement输入:
<?xml version="1.0"?>
<Item Number="100" ItemName="TestName1" ItemId="1"/>
使用项目模型,如:
public class Item
{
public int ItemId { get; set; }
public string ItemName { get; set; }
public int? Number { get; set; }
// public DateTime? Created {get; set;}
}
为什么这段代码:
public static T DeserializeObject<T>(XElement element) where T : class, new()
{
try
{
var serializer = new XmlSerializer(typeof(T));
var x = (T)serializer.Deserialize(element.CreateReader());
return x;
}
catch
{
return default(T);
}
}
返回具有默认值的Item模型:ItemId = 0,ItemName = null,Number = null而不是正确的值。
这可以通过向模型添加属性[XmlAttribute(&#34; ItemName&#34;)]来修复,但我不想要XmlAttributes。
向Item模型添加可为空的DateTime字段也会导致反序列化异常,即使它具有XmlAttribute也是如此。
我在JSON.net中有相同的代码,其中我正在做的是JToken上的p.ToObject()以将其反序列化为Item对象。是否有另一种技术或反序列化器可以更好地处理这个问题,而无需使用属性等来处理它并处理可以为空的值。对于XML版本来说应该也很容易。
请仔细考虑这个问题,因为我有另一个类似的问题关闭[重复],其中没有一个答案实际涵盖了我的要求。
答案 0 :(得分:1)
似乎如果不使用XML属性修饰C#类属性,则反序列化器假定属性是元素而不是属性。
string xml = "<Item Number=\"100\" ItemName=\"TestName1\" ItemId=\"1\"><Number>999</Number></Item>";
XElement el = XElement.Parse(xml);
var obj = DeserializeObject<Item>(el); // Returns Number equal to 999 while others are left null or 0
答案 1 :(得分:1)
您可以尝试Cinchoo ETL - 一个满足您需求的开源库。
string xml = @"<?xml version=""1.0""?>
<Item Number = ""100"" ItemName = ""TestName1"" ItemId = ""1"" />";
XDocument doc = XDocument.Parse(xml);
var item = ChoXmlReader<Item>.LoadXElements(new XElement[] { doc.Root }).FirstOrDefault();
Console.WriteLine($"ItemId: {item.ItemId}");
Console.WriteLine($"ItemName: {item.ItemName}");
Console.WriteLine($"Number: {item.Number}");
Console.WriteLine($"Created: {item.Created}");
希望它有所帮助。
免责声明:我是这个图书馆的作者。
答案 2 :(得分:0)
我最终编写了下面的自定义反序列化器,类似于jToken的json反序列化器方法。它应该适用于具有简单类型属性(如string,int,datetime等)的基本扁平对象以及这些类型的可空版本。它不需要XmlAttributes。
public static T ToOject<T>(this XElement element) where T : class, new()
{
try
{
T instance = new T();
foreach (var property in typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
var xattribute = element.Attribute(property.Name);
var xelement = element.Element(property.Name);
var propertyType = Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType;
var value = xattribute?.Value ?? xelement.Value;
try
{
if (value != null)
{
if (property.CanWrite)
{
property.SetValue(instance, Convert.ChangeType(value, propertyType));
}
}
}
catch // (Exception ex) // If Error let the value remain default for that property type
{
Console.WriteLine("Not able to parse value " + value + " for type '" + property.PropertyType + "' for property " + property.Name);
}
}
return instance;
}
catch (Exception ex)
{
return default(T);
}
}
当您知道什么是,您可以写下以下内容:
var list = xdocument.Descendants("Item")
.Select(p => p => p.ToOject<T>())
.ToList();
答案 3 :(得分:0)
我对一个由大约30个嵌套对象组成的对象也遇到了类似的问题。大多数属性将被序列化为属性。我确实使用了XmlAttributeOverrides,但是代码很简单。基本上,我从特定命名空间中所有类的所有属性中获得所有属性,只保留简单的类型属性(值类型和字符串),然后滤除所有已具有序列化属性的属性。然后,我将XmlAttribute应用于所有这些属性。就我而言,将没有结构类型,所以这不是问题,但是您可能也必须过滤掉结构。
//Note that the serializer is created just once in the application; this is to prevent
//a known memory leak with the XmlSerializer when using
//the constructor that takes XmlAttributeOverrides
private static XmlSerializer serializer = new XmlSerializer(typeof(MyClass), GetAttributeOverrides());
public static MyClass GetObject(string xml)
{
using (var reader = new StringReader(xml))
{
return (MyClass)serializer.Deserialize(reader);
}
}
public static XmlAttributeOverrides GetAttributeOverrides()
{
var overrides = new XmlAttributeOverrides();
var simpleProperties = (from t in Assembly.GetExecutingAssembly().GetTypes()
where t.IsClass && t.Namespace == typeof(MyClass).Namespace
from p in t.GetProperties()
where IsSimpleProperty(p)
select p);
foreach (var prop in simpleProperties)
{
var attrs = new XmlAttributes() { XmlAttribute = new XmlAttributeAttribute() };
overrides.Add(prop.DeclaringType, prop.Name, attrs);
}
return overrides;
}
public static bool IsSimpleProperty(PropertyInfo prop)
{
return (prop.PropertyType.IsValueType || prop.PropertyType == typeof(string))
&& !prop.CustomAttributes.Any(a => a.AttributeType.Namespace == "System.Xml.Serialization");
}