22

I have the following C# classes :

public class Books
{

public List<Book> BookList;

}

public class Book
{

public string Title;
public string Description;
public string Author;
public string Publisher;

}

How can I serialize this class into the following XML?

<Books>
  <Book Title="t1" Description="d1"/>
  <Book Description="d2" Author="a2"/>
  <Book Title="t3" Author="a3" Publisher="p3"/>
</Books>

I want the XML to have only those attributes whose values are not null/empty. For example: In the first Book element, author is blank, so it should not be present in the serialized XML.

Alfred Myers
  • 6,384
  • 1
  • 40
  • 68
user40907
  • 1,532
  • 5
  • 24
  • 33

4 Answers4

47

You should be able to use the ShouldSerialize* pattern:

public class Book
{
    [XmlAttribute] 
    public string Title {get;set;}

    public bool ShouldSerializeTitle() {
        return !string.IsNullOrEmpty(Title);
    }

    [XmlAttribute]
    public string Description {get;set;}

    public bool ShouldSerializeDescription() {
        return !string.IsNullOrEmpty(Description );
    }

    [XmlAttribute]
    public string Author {get;set;}

    public bool ShouldSerializeAuthor() {
        return !string.IsNullOrEmpty(Author);
    }

    [XmlAttribute]
    public string Publisher {get;set;}

    public bool ShouldSerializePublisher() {
        return !string.IsNullOrEmpty(Publisher);
    }
}
T.S.
  • 18,195
  • 11
  • 58
  • 78
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • 3
    I was unclear on how the above solution worked until I read the following post: http://kjellsj.blogspot.com/2006/02/conditional-xml-serialization_08.html – Dean Nov 02 '11 at 15:59
  • 2
    +1 Coolest thing I never knew about :) Just solved a particularly sticky backward-compatibility problem with this gem. – iCollect.it Ltd Sep 12 '13 at 09:17
  • 2
    Note that ShouldSerialize method **should** be public here, unlike other cases like PropertyGrid serialization control where the method can be private and yet not be ignored. – Stelios Adamantidis Apr 03 '14 at 17:13
8

Alternative :

  • Switch your public fields to properties
  • Define default values with the DefaultValueAttribute attribute
  • Define content property with the ContentPropertyAttribute attribute
  • Use XamlWriter/XamlReader

You end up with something like this:

 [ContentProperty("Books")]
 public class Library {

   private readonly List<Book> m_books = new List<Book>();

   public List<Book> Books { get { return m_books; } }

 }

 public class Book
 {

    [DefaultValue(string.Empty)]
    public string Title { get; set; }

    [DefaultValue(string.Empty)]
    public string Description { get; set; }

    [DefaultValue(string.Empty)]
    public string Author { get; set; }

 }
Nicolas Repiquet
  • 9,097
  • 2
  • 31
  • 53
3
public class Books
{
    [XmlElement("Book")]
    public List<Book> BookList;
}

public class Book
{
    [XmlAttribute]
    public string Title;
    [XmlAttribute]
    public string Description;
    [XmlAttribute]
    public string Author;
    [XmlAttribute]
    public string Publisher;
}

class Program
{
    static void Main()
    {
        var books = new Books
        {
            BookList = new List<Book>(new[] 
            {
                new Book 
                {
                    Title = "t1",
                    Description = "d1"
                },
                new Book 
                {
                    Author = "a2",
                    Description = "d2"
                },
                new Book 
                {
                    Author = "a3",
                    Title = "t3",
                    Publisher = "p3"
                },
            })
        };

        var serializer = new XmlSerializer(books.GetType());
        serializer.Serialize(Console.Out, books);
    }
}

And if you want to remove the namespace from the root node:

var namespaces = new XmlSerializerNamespaces();
namespaces.Add(string.Empty, string.Empty);
serializer.Serialize(Console.Out, books, namespaces);

Also I would recommend you using properties instead of fields in your model classes for better encapsulation:

public class Books
{
    [XmlElement("Book")]
    public List<Book> BookList { get; set; }
}
Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • 5
    Is this actually answering the question about skipping the null/blank items? – Will Dean Nov 04 '10 at 09:00
  • Indeed my code works for null elements, but it doesn't work for blank elements (they will still be present in the generated XML). – Darin Dimitrov Nov 04 '10 at 10:41
  • I believe, when u use properties, by default, the value is null and hence the attributes wont be present in the generated xml unless they r set to empty or any other value. – user40907 Nov 04 '10 at 12:12
  • Fields are also `null` by default for reference types and the default value for value types. – Darin Dimitrov Nov 04 '10 at 12:27
2

Another option is to use "Specified"-property like (works like "ShouldSerialize()"):

public bool <<Your XML-property name here>>Specified
{
    get { return <<your condition here (i.e. not null check)>>; }
}
Martin.Martinsson
  • 1,894
  • 21
  • 25