0

I am trying to deserialize an xml list to a generic List in C#. I do not want to use XmlAttributes as the solution needs to be generic. I must be getting close as I am getting 3 Item objects but they are all populated with default values, ie: null or 0.

This is not a duplicate of how to deserialize an xml node with a value and an attribute using asp.net serialization which uses XmlAttributes and is not an option. This is clearly stated in the question.

There are a number of SO answers out there whicha I have tried and are similar to what I need, including the other list of SO responses in the [Duplicate]. The code below is modified from one of those answers but does not quite work.

<?xml version="1.0" encoding="utf-8"?>
<root>
---snip---
    <Items>
    <Item ItemId="1" ItemName="TestName1" Number="100" Created=""></Item>
    <Item ItemId="2" ItemName="TestName2" Number="200" Created=""></Item>
    <Item ItemId="3" ItemName="TestName3" Number="300" Created=""></Item>
    </Items>
</root>

========================================================

var sourceData = XDocument.Parse(xml); 

public List<T> GetObjectList<T>(string identifier) where T : class, new()
{
    if (sourceData != null && !identifier.isNullOrEmpty())
    {
        var list = sourceData.Descendants(identifier)
            .Select(p => DeserializeObject<T>(p.ToString()))
            .ToList();

        return list;
    }
    return new List<T>();
}

public static T DeserializeObject<T>(string xml)  where T : class, new()
{
    if (string.IsNullOrEmpty(xml))
    {
        return default(T);
    }
    try
    {
        using (var stringReader = new StringReader(xml))
        {
            var serializer = new XmlSerializer(typeof(T));
            return (T) serializer.Deserialize(stringReader);
        }
    }
    catch 
    {
        return default(T);
    }
}

public class Item
{
    public int ItemId { get; set; }
    public string ItemName { get; set; }
    public int? Number { get; set;}
    public DateTime? Created { get; set; }
}

// RESULT
// Item ItemId="0" ItemName="null" Number="null" Created="null" 
// Item ItemId="0" ItemName="null" Number="null" Created="null"
// Item ItemId="0" ItemName="null" Number="null" Created="null"
WillC
  • 1,761
  • 17
  • 37
  • This is not a duplicate of any of those questions and I have looked over many of the answers without finding a solution. I can use XmlAttributes and remove the Created field to make the above example work, but I need it work better than that. – WillC Jan 06 '18 at 18:49
  • 1
    If you can't modify your `Item` type to add the `[XmlAttribute]` attribute, you could preprocess your XML to convert all attributes to elements as shown in [C# to convert xml attributes to elements](https://stackoverflow.com/q/6496334/3744182). By the way, since you have already loaded your XML into an `XDocument`, you can deserialize directly from an `XElement` without converting back to a string by using `XObjectExtensions.Deserialize()` from [C#: Best way to have XML element name from generic type name](https://stackoverflow.com/a/38346726/3744182). – dbc Jan 06 '18 at 23:34
  • Might the default value be created by `return default(T);` -- especially the second one, just making all exceptions disappear without logging them? (Especially the "Attribute is missing"-Exceptions.) – phi1010 Jan 11 '18 at 02:20

2 Answers2

1

Check out the XmlSerialzer constructor, you can specify attribute overrides there, if you do not want to modify the class -- you'll need some data defining what you serialize at some point; the XmlSerializer was never written to work without these Attributes, or types that are known to be serializable (numbers, strings, ...).

If you want to be generic, you'll have to write your own serializer, which uses reflection to decide what to serialize, how, and in which order, and what to ignore. You can use the XmlSerializer in it when serializing types that have the necessary attributes. Search for tutorials explaining how to implement IXmlSerializable, they will show you how to use XmlWriter / XmlReader to avoid handling the XML syntax yourself.

A guess why it is like this: Circular references make (de-)serialization really hard, and a generic solution would probably try to serialize this without coming to an end, causing weird bugs with the call hanging forever, when some developer changes one of those classes without being able to see (due to no attribute being there) that they have to write the class in a way avoiding serialization problems.

phi1010
  • 678
  • 6
  • 13
  • I'm kind of late with the points but thanks for the explanation. This pretty much answers this question and was what I was expecting by that point. – WillC Feb 14 '18 at 04:21
0

I asked another similar question (A Better XElement to Object Without XMLAttributes in C#) when this one got marked as a duplicate (for a while) and ended up creating a simple custom de-serializer that met my needs. See the answers there for the code if interested.

WillC
  • 1,761
  • 17
  • 37