How to serialize/deserialize optional XML enumerat

2019-01-14 10:04发布

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!

3条回答
Luminary・发光体
2楼-- · 2019-01-14 10:14

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.

查看更多
3楼-- · 2019-01-14 10:16

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;

}
查看更多
对你真心纯属浪费
4楼-- · 2019-01-14 10:29

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
查看更多
登录 后发表回答