This is simple using XmlSerialization.
For serialization to work, you create a C# class to represent each level of xml nodes that contain children. For your schema, we need three classes: Bookstore, Book, and Author.
Here's the code that calls the serialization Bookstore class:
Here's the output:Code:static void Main( string [ ] args ) { var bookStore = BookStore.Read( "bookstore.xml" ); foreach( var book in bookStore.Books ) { DisplayBook( book ); } } static void DisplayBook( Book book ) { Console.WriteLine( String.Format( "Title: {0}", book.Title ) ); Console.WriteLine( String.Format( "\tGenre: {0}", book.Genre ) ); Console.WriteLine( String.Format( "\tPrice: {0}", book.PriceType ) ); if ( !String.IsNullOrEmpty( book.Author.Name ) ) Console.WriteLine( String.Format( "\tName: {0}", book.Author.Name ) ); if ( !String.IsNullOrEmpty( book.Author.NameFirst ) ) Console.WriteLine( String.Format( "\tFirst: {0}", book.Author.NameFirst ) ); if ( !String.IsNullOrEmpty( book.Author.NameLast ) ) Console.WriteLine( String.Format( "\tLast: {0}", book.Author.NameLast ) ); Console.WriteLine( String.Format( "\tPrice: {0}\n", book.Price ) ); }
The Bookstore class contains a static Read method which does all the work of creating the XmlSerializer class, reading the bookstore.xml file and returning an instance of the BookStore class. Notice the Xml attributes at the top of the class declaration. These are what allows use to define the top level serialization class as well as name the class differently for the xml note (i.e. "bookstore" becomes the BookStore class).Code:Title: The Autobiography of Benjamin Franklin Genre: autobiography Price: great First: Benjamin Last: Franklin Price: 8.99 Title: The Confidence Man Genre: novel Price: average First: Herman Last: Melville Price: 11.99 Title: The Gorgias Genre: philosophy Price: best Name: Plato Price: 9.99
Notice how the books node has been represented as an Array of Book objects? You might also notice the XmlElement attribute has specified "book" to coincide with the 'book' node. If we didn't specify 'book', the serializer would attempt to look for a 'Book' node and fail to load. Lastly, notice how a generic List<Book> is used rather than a raw array. This allows us to call the Books property and use it without having to check for null first. Using the List<Book> guarantees that the Books property will never be null.Code:/// <summary> /// Serialization class used to read the bookstore.xml file /// </summary> [Serializable] [XmlType( AnonymousType = true )] [XmlRoot( ElementName = "bookstore", Namespace = "", IsNullable = false )] public class BookStore { /// <summary/> public static BookStore Read( string configFile ) { var serializer = new XmlSerializer( typeof( BookStore ) ); using ( var reader = XmlReader.Create( configFile ) ) { return ( BookStore ) serializer.Deserialize( reader ); } } /// <remarks/> [XmlElement( "book", IsNullable = false )] public Book [ ] Books { get { return _bookList.ToArray( ); } set { if ( null == _bookList ) { _bookList = new List<Book>( ); } _bookList.AddRange( value ); } } private List<Book> _bookList = new List<Book>( ); }
On to the Book class:
Notice how the properties are decorated with either XmlAttribute or XmlElement C# attributes? These are what defines whether the xml item is an attribute or an element. This also allows us to solve your particular problem because it's okay to have an attribute with the same name as an element in the same parent node. The XmlSerializer reads it fine. Rather than specifying a string type for Genres and [the attribute] price, I've taken the liberty to define a couple of enums (Genres and PriceTypes). If you have a fixed number of both, I would recommend taking the enum approach (vs. just leaving genre and price as a string type).Code:/// <remarks/> [Serializable] [XmlType( AnonymousType = true )] public class Book { /// <remarks/> [XmlAttribute( AttributeName = "genre" )] public Genres Genre { get { return _genre; } set { _genre = value; } } /// <remarks/> [XmlAttribute( AttributeName = "price" )] public PriceTypes PriceType { get { return _priceType; } set { _priceType = value; } } [XmlElement( "title", IsNullable = false )] public string Title { get { return _title; } set { _title = value; } } [XmlElement( "author", IsNullable = false )] public Author Author { get { return _author; } set { _author = value; } } [XmlElement( "price", IsNullable = false )] public double Price { get { return _price; } set { _price = value; } } private Genres _genre; private PriceTypes _priceType; private string _title; private Author _author; private double _price; }
Finally, the Author class. Nothing new here.
See the attachment for the complete source.Code:/// <remarks/> [Serializable] [XmlType( AnonymousType = true )] public class Author { [XmlElement( "first-name", IsNullable = false )] public string NameFirst { get { return _nameFirst; } set { _nameFirst = value; } } [XmlElement( "last-name", IsNullable = false )] public string NameLast { get { return _nameLast; } set { _nameLast = value; } } [XmlElement( "name", IsNullable = false )] public string Name { get { return _name; } set { _name = value; } } private string _name; private string _nameFirst; private string _nameLast; }
__________________________________________________________
Arjay
See my latest series on using WCF to communicate between a Windows Service and WPF task bar application.
Tray Notify - Part I Tray Notify - Part II
Need a little help with Win32 thread synchronization? Check out the following CG articles and posts:
Sharing a thread safe std::queue between threads w/progress bar updating
Simple Thread: Part I Simple Thread: Part II
Win32 Thread Synchronization, Part I: Overview Win32 Thread Synchronization, Part 2: Helper Classes
www.iridyn.com
![]()
...




Reply With Quote