Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can [XmlText] serialization be nullable?

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?

like image 232
ladenedge Avatar asked Sep 05 '12 14:09

ladenedge


2 Answers

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.

like image 71
akton Avatar answered Oct 19 '22 11:10

akton


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; }

}
like image 1
denvercoder9 Avatar answered Oct 19 '22 11:10

denvercoder9