Rockdocta
Rockdocta

Reputation: 614

How to serialize/deserialize optional XML enumeration in C#?

I am trying to figure out how to serialize/deserialize an XML listing to C# that has an optional attribute that is an enumerated type. The following is my C# class:

public class AttributeAssignmentExpressionElement : XACMLElement
{
    [XmlAttribute]
    public string AttributeId { get; set; }

    [XmlAttribute]
    public Category Category { get; set; }                   
}

My Category enumeration is defined as follows:

public enum Category
{
    [XmlEnum(Name = "urn:oasis:names:tc:xacml:1.0:subject-category:access-subject")]
    Subject,
    [XmlEnum(Name = "urn:oasis:names:tc:xacml:3.0:attribute-category:resource")]
    Resource,
    [XmlEnum(Name = "urn:oasis:names:tc:xacml:3.0:attribute-category:action")]
    Action,
    [XmlEnum(Name = "urn:oasis:names:tc:xacml:3.0:attribute-category:environment")]        
    Environment
}  

When Category is present in the corresponding XML file, serialization/deserialization works as expected. However if the Category is missing from the XML, the default value is used (first item in the enumeration). If I try to make the enumerated variable nullable (Category?), the deserializer throws an exception because it is unable to deserialize a complex type. Given the following XML (which does not contain the attribute), how can I serialize the enumeration appropriately?

<AttributeAssignmentExpression
    AttributeId="urn:oasis:names:tc:xacml:3.0:example:attribute:text">       
</AttributeAssignmentExpression>

In this situation, the value in the deserialized object should be null.

Thanks for any help you can offer!

Upvotes: 7

Views: 11252

Answers (3)

Oleg Polezky
Oleg Polezky

Reputation: 1132

The complete sample code using 'Specified' pattern

public class ClassToSerialize
{
    [XmlAttribute("attributeName")]
    public EnumType EnumPropertyValue
    {
        get { return EnumProperty.Value; }
        set { EnumProperty = value; }
    }
    [XmlIgnore]
    public EnumType? EnumProperty { get; set; }
    public bool EnumPropertyValueSpecified => EnumProperty.HasValue;

}

Upvotes: 3

lxa
lxa

Reputation: 3332

Actually, there's some official magic which allows to do this (see here):

Another option is to use a special pattern to create a Boolean field recognized by the XmlSerializer, and to apply the XmlIgnoreAttribute to the field. The pattern is created in the form of propertyNameSpecified. For example, if there is a field named "MyFirstName" you would also create a field named "MyFirstNameSpecified" that instructs the XmlSerializer whether to generate the XML element named "MyFirstName". This is shown in the following example.

That is, the model in TS case should look like this:

public class AttributeAssignmentExpressionElement : XACMLElement
{
    [XmlAttribute]
    public string AttributeId { get; set; }

    [XmlAttribute]
    public Category Category { get; set; }

    [XmlIgnore]
    public bool CategorySpecified { get; set; }                   
}

Unless you set magic field CategorySpecified to true, Category attribute won't be serialized. In case of deserialization, CategorySpecified will be false, indicating that Category wasn't present in XML.

Upvotes: 4

Marc Gravell
Marc Gravell

Reputation: 1063068

Well, you can do this - but it is a bit messy:

[XmlIgnore]
public Category? Category { get; set; }

[XmlAttribute("Category")]
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public Category CategorySerialized
{
    get { return Category.Value; }
    set { Category = value; }
}
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public bool ShouldSerializeCategorySerialized()
{
    return Category.HasValue;
}

What this does:

  • uses a Category? for the optional enum value
  • disables the Category property for serialization
  • adds a secondary property, CategorySerialized, as a proxy to Category, which is non-nullable and hidden (as far as is possible) from the IDE etc
  • use conditional serialization on CategorySerialized via the ShouldSerialize* pattern

Upvotes: 15

Related Questions