XmlSerializer Deserialize XML with additional reference

79 views Asked by At

I have a XML like this:

<Data>
<Author>
    <AuthorID>1</AuthorID>
    <Name>Max</Name>
</Author>
<Author>
    <AuthorID>2</AuthorID>
    <Name>Steve</Name>
</Author>
<Book>
    <AuthorID>1</AuthorID>
    <Name>Book name</Name>
</Book>
<Book>
    <AuthorID>2</AuthorID>
    <Name>Book name 2</Name>
</Book>

When i use the "insert xml as class" function from visual studio i get classes like this:

public class Data() {
    private DataAuthor[] dataAuthorFields;
    private DataBook[] dataBookFields;
}
public class DataAuthor() {
    private string AuthorID;
    private string Name;
}

I have now two lists and can easy show all authors and all books. But additional i want to have a data model where i can get all books from one author:

public class DataAuthor() {
    private string AuthorID;
    private string Name;
    **private DataBook[] Books;**
}

I can't change XML-File and just need one reference to all books from one auther. How i can do this properly?

2

There are 2 answers

2
Neil Stevens On

Try using Linq

var books = data.DataBook.Where(x => x.AuthorID == 1).ToArray();
0
steve16351 On

You could call a method on the Data class after deserialization to assign the books to the authors, so you only pay for the cost of doing that assignment once and store the result. Or, if you want to keep the calling code clean, you could lazy initialise on the getter for the Authors property on the data class (since it will only be called after deserialization), and do that assignment upon the first access of the authors property. The XmlIgnore attribute means the Books property on Author won't mess up your xml if you serialize back to xml.

Usage:

using (XmlReader xmlReader = new XmlTextReader("somefile.xml"))
{
    XmlSerializer serializer = new XmlSerializer(typeof(Data));
    Data dataModel = serializer.Deserialize(xmlReader) as Data;

    foreach (Author author in dataModel.Authors)
    {
        Console.WriteLine("{0} has {1} books.", author.Name, author.Books.Count());
    }
}         

The classes for the lazy initialise option would look something like this:

public class Data
{
    public Data()
    {
        _authorsWithBooks = new Lazy<Author[]>(() => _authorData.Select(a => { a.Books = Books.Where(b => b.AuthorID == a.AuthorID).ToArray(); return a; }).ToArray());
    }

    private Author[] _authorData;
    private Lazy<Author[]> _authorsWithBooks;

    [XmlElement("Author")]
    public Author[] Authors
    {
        get
        {
            return _authorsWithBooks.Value;
        }
        set
        {
            _authorData = value;
        }
    }

    [XmlElement("Book")]
    public Book[] Books { get; set; }
}


public class Author
{
    public string AuthorID { get; set; }
    public string Name { get; set; }
    [XmlIgnore]
    public Book[] Books { get; set; }
}

public class Book
{
    public string AuthorID { get; set;}
    public string Name { get; set; }
}