views:

178

answers:

1

I have a Repository Class with the following method...

public T Single<T>(Predicate<T> expression)
{
    using (var list = (Models.Collectable<T>)System.Xml.Serializer.Deserialize(typeof(Models.Collectable<T>), FileName))
    {
        return list.Find(expression);
    }
}

Where Collectable is defined..

[Serializable]
public class Collectable<T> : List<T>, IDisposable
{
    public Collectable() { }

    public void Dispose() { }
}

And an Item that uses it is defined..

[Serializable]
[System.Xml.Serialization.XmlRoot("Titles")]
public partial class Titles : Collectable<Title>
{
}

The problem is when I call the method, it expects "Collectable" to be the XmlRoot, but the XmlRoot is "Titles" (all of object Title).

I have several classes that are collected in .xml files like this, but it seems pointless to rewrite the basic methods for loading each up when the generic accessors do it - but how can I enforce the proper root name for each file without hard coding methods for each one? The [System.Xml.Serialization.XmlRoot] seems to be ignored.

When called like this...

var titles = Repository.List<Models.Title>(); 

I get the exception

<Titlesxmlns=''> was not expected. 

The XML is formatted such as. ..

<?xml version="1.0" encoding="utf-16"?>
<Titles xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"&gt;
    <Title>
        <Id>442daf7d-193c-4da8-be0b-417cec9dc1c5</Id>
    </Title>
</Titles>

Here is the deserialization code.

  public static T Deserialize<T>(String xmlString)
    {
        System.Xml.Serialization.XmlSerializer XmlFormatSerializer
            = new System.Xml.Serialization.XmlSerializer(typeof(T));

        StreamReader XmlStringReader = new StreamReader(xmlString);

        //XmlTextReader XmlFormatReader = new XmlTextReader(XmlStringReader);

        try
        {
            return (T)XmlFormatSerializer.Deserialize(XmlStringReader);
        }
        catch (Exception e)
        {
            throw e;
        }
        finally
        {
            XmlStringReader.Close();
        }
    }
+1  A: 

It makes no sense for the root of a document to be a collection. It may contain a collection, but it cannot be one.


Your problem appears to be simpler. You cannot create a serializer using an open generic type (Models.Collectable<T>). Try using typeof(Titles) instead.


I'm making some assumptions about your repository classes, but would something like this work?

public class Repository<T>
{
    protected static TextReader FileName { get; set; }

    public static TCollection List<TCollection>()
    {
        var ser = new XmlSerializer(typeof (TCollection));
        return (TCollection) ser.Deserialize(FileName);
    }

    public static TItem Single<TItem, TCollection>(Predicate<TItem> expression) 
        where TCollection : IDisposable, IEnumerable<TItem>
    {
        using (var list = List<TCollection>())
        {
            return list.Single(item => expression(item));
        }
    }
}
John Saunders
I'm not sure I understand. I have just called the Serializer on a Collection, and that's how it outputs the file. It's always worked before, this is just the first time I've tried to use Generics with it.
Stacey
Have you always used `XmlRoot`?
John Saunders
Yes, I have always used that on all of my classes that are intended for serialization.
Stacey
Yes, that does work. Except the goal is to avoid writing 15 repositories for all of the various types.
Stacey
@Stacey: I don't understand. You already have the `Titles` type. Just use it in the `typeof` expression. If necessary, change your `Single` method to take both the collection type and the collected type.
John Saunders
Then I have to have the same method twice. I have over 15 of these types of collections stored in XML files. I need to have repositories for ALL of them. If I hardcode the type, then I'm just repeating myself 14 more times, and increasing the workload and margin for error in editing and maintaining.
Stacey
@Stacey: your underlying problem is that it's necessary to deserialize the exact same type that you serialized. You want to serialize `Titles` but deserialize `Collectable<Title>`. You can't do that.
John Saunders
It worked! Oh my heavens! I see what you're doing. I think I was on the right track, but I wasn't thinking abstract enough. Thank you so much, this will help immensely. So what you're doing is passing collections that were serialized as the types, which will return the objects within it ,but you're also accepting the type itself for singular construction?
Stacey
@Stacey: that's one way to think of it. I find it simpler to thing that I have to deserialize the same type I serialized. In your case, this means the `XmlSerializer` instance you create to serialize must be created with the same `Type` parameter as the one you created to deserialize. In your scenario, that means passing the type as a type parameter.
John Saunders
Yes! It makes sense now! I just didn't think far enough ahead. Thank you so much. Both of you. I've learned so much about serialization and generics tonight!
Stacey