问题
Given an XElement input:
<?xml version="1.0"?>
<Item Number="100" ItemName="TestName1" ItemId="1"/>
with an Item model like:
public class Item
{
public int ItemId { get; set; }
public string ItemName { get; set; }
public int? Number { get; set; }
// public DateTime? Created {get; set;}
}
Why does this code:
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);
}
}
Returns an Item model with default values: ItemId=0, ItemName=null, Number=null instead of the correct values.
This can be fixed by adding attributes to the model [XmlAttribute("ItemName")] but I do not want to require XmlAttributes.
The addition of a nullable DateTime field to the Item model also causes a deserialization exception, even if it has an XmlAttribute.
I have the equivalent code in JSON.net where all I am doing is p.ToObject() on the JToken to deserialize it to an Item object. Is there another technique or deserializer that handles this better without having to qualify with attributes, etc. and that handles nullable values. It should be that easy for the XML version too.
Please consider this question carefully, as I had another similar question closed as [Duplicate] where none of the answers actually covered what I was asking.
回答1:
It seems if you don't decorate your C# class properties with the XML attributes, the deserializer assumes the properties are elements and not attributes.
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
回答2:
You can try Cinchoo ETL - an open source lib for your needs.
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}");
Hope it helps.
Disclaimer: I'm the author of this library.
回答3:
I ended up writing the custom deserializer below that is similar to the json deserializer method for jToken. It should work for basic, flat objects that have simple type properties like string, int, datetime, etc. and the nullable versions of those types. It does not require 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);
}
}
When you know what is, you can write the following:
var list = xdocument.Descendants("Item")
.Select(p => p => p.ToOject<T>())
.ToList();
来源:https://stackoverflow.com/questions/48130944/a-better-xelement-to-object-without-xmlattributes-in-c-sharp