7

I have a class containing an array I wish to serialize with XmlSerializer:

[XmlArray("properties")]
[XmlArrayItem("property", IsNullable = true)]
public List<Property> Properties { get; set; }

Property is a class containing an attribute and some XmlText:

[XmlAttribute("name")]
public string Name { get; set; }

[XmlText]
public string Value { get; set; }

The problem is that when Value is null, it serializes as an empty string:

<property name="foo" />

rather than a null. I'm looking for the value to either be omitted entirely, or look like this:

<property name="foo" xsi:nil="true" />

Is it possible to null out an element in a list based on its XmlText value? I'm really trying to avoid custom serialization, but perhaps some other serialization framework would be better in this case?

ladenedge
  • 13,197
  • 11
  • 60
  • 117
  • Just firing from the hip - try using `public string? Value` instead of just `public string Value` and let us know if it makes a difference. – Filip Vondrášek Sep 07 '12 at 16:42
  • 1
    `string` is a reference type, so I'm afraid it [isn't nullable in that way](http://stackoverflow.com/a/9236474/222481). – ladenedge Sep 07 '12 at 17:58
  • Ah, I see. Does [this](http://bytes.com/topic/c-sharp/answers/558137-xmlserializer-empty-element-null-value) help in any way? – Filip Vondrášek Sep 07 '12 at 18:19

2 Answers2

2

Use the IsNullable=true in the XmlArrayItemAttribute class. For an example.

[XmlRoot("Root")]
public class Root
{
    [XmlArrayItem("Element", IsNullable = true)]
    public string[] Elements { get; set; }
}

Some sample code in Visual Studion 2012 and .Net 4.5:

using System.Xml.Serialization;

...

// Test object
Root root;
root = new Root();
root.Elements = new string[] { null, "abc" };

using(MemoryStream stream = new MemoryStream())
{
    XmlSerializer xmlSerializer = new XmlSerializer(typeof(Root));
    xmlSerializer.Serialize(stream, root);

    Console.WriteLine(new string(Encoding.UTF8.GetChars(stream.GetBuffer())));
}

The output is (line breaks added for clarity):

<?xml version="1.0"?>
<Root 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Element>
    <string xsi:nil="true" />
    <string>abc</string>
  </Element>
</Root>

And with a complex type (also in .Net 4.5 on Visual Studio 2012):

    public class MyProperty
    {
        public string Foo { get; set; }
    }

    [XmlRoot("Root")]
    public class Root
    {
        [XmlArrayItem("Element", IsNullable = true)]
        public MyProperty[] Elements { get; set; }
    }

    ,,,

    Root root;
    root = new Root();
    root.Elements = new MyProperty[] { null, new MyProperty{ Foo = "bar" } };

    // Other code is as above

Using the same code above produces:

<?xml version="1.0"?>
<Root 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Elements>
    <Element xsi:nil="true" />
    <Element>
      <Foo>bar</Foo>
    </Element>
  </Elements>
</Root>

Also remember that the type must be a reference type (not a struct, for example) to write out xsi:nil=true.

akton
  • 14,148
  • 3
  • 43
  • 47
  • I'm not using `XmlElement` at all. I have a schema, but I'm afraid I don't understand how that would help..? – ladenedge Sep 10 '12 at 01:20
  • I have `IsNullable = true` on my `[XmlArrayItem]` declaration. It seems to work differently when the array items are a complex type. – ladenedge Sep 12 '12 at 05:40
  • @ladenedge I tried with a complex type (see updated answer) and it still output with `xsi:nil="true"`. May this is a bug in earlier versions of the framework or the complex type is a struct or other value type. – akton Sep 12 '12 at 09:40
  • Sorry, I'm not being clear. This question is about setting an element to *nil* when its `[XmlText]` is null, not when the array item itself is null. – ladenedge Sep 12 '12 at 18:08
  • @ladenedge When Value is null, you want Value to be serialized to XML as nil or the Property to be omitted entirely from serialization? If the former, you can do that if Value is serialized as an XML element, as mentioned in my answer but not if it is an XML attribute. If the latter, you could temporarily remove the Property objects from the List beforehand using a method with OnSerializatoinAttribute and add it back in a method using OnDeserializationAttribute. – akton Sep 13 '12 at 01:50
  • Omitting the property entirely via `OnSerialization` satisfies my requirement. I do still wonder if there is a way to get ``.. anyway, thanks for all your time! – ladenedge Sep 14 '12 at 14:12
1

One thought - you could use "NameSpecified", but check the value of "Value" in the get. Meaning, if Value is null, then Name will not be output either.

Unfortunately, you will still have a null property xml element, tho; I hope that's more acceptable...

class Program
{
    static void Main(string[] args)
    {
        ObjectWithProperties obj = new ObjectWithProperties()
        {
            Properties = new List<Property>()
        };

        Property p = new Property();
        p.Name = "This WILL Show Up";
        p.Value = "I'm here";
        obj.Properties.Add(p);

        Property p1 = new Property();
        p1.Name = "This Will NOT Show Up";
        obj.Properties.Add(p1);

        Console.WriteLine(ToXmlString(obj));
        Console.ReadLine();
    }

    public static string ToXmlString(object value)
    {
        if (value == null) return string.Empty;
        XmlSerializer ser = new XmlSerializer(value.GetType());
        MemoryStream ms = new MemoryStream();
        ser.Serialize(ms, value);
        return Encoding.UTF8.GetString(ms.ToArray());
    }

}
public class ObjectWithProperties
{
    [XmlArray("properties")]
    [XmlArrayItem("property", IsNullable = true)]
    public List<Property> Properties { get; set; }
}

public class Property
{
    [XmlAttribute("name")]
    public string Name { get; set; }

    [XmlIgnore]
    public bool NameSpecified
    {
        get { return !string.IsNullOrEmpty(Value); }
    }

    [XmlText]
    public string Value { get; set; }

}
denvercoder9
  • 801
  • 7
  • 15
  • Unfortunately `` is [the empty string, not null](http://publib.boulder.ibm.com/infocenter/db2luw/v9/index.jsp?topic=%2Fcom.ibm.db2.udb.apdv.embed.doc%2Fdoc%2Fc0022618.htm). – ladenedge Sep 10 '12 at 01:19