简体   繁体   中英

Handling extra spaces when deserializing XML values to enums

I've been wondering if it's possible to do this.

It would be a great help in cases where an XML response has incorrect values that are needing to be mapped to enums.

The case I'm dealing with specifically is when an expected value has a trailing space and the enum is expecting it without.

XML:

<Foo>
    <Bar>EnumValue </Bar>
</Foo>

Enum:

public enum MyEnum
{
    [XmlEnum("EnumValue")]
    EnumValue
}

Class:

public class Foo
{
    [XmlElement("Bar")]
    public MyEnum myEnum { get; set; }
}

I've investigated using a custom attribute (instead of "XmlEnum") to trim the values but it doesn't seem to be reached during the deserialization.

Is there a way to trim XML values (when needed) before/during deserialization so that the value can be mapped to the enum correctly?

-

I should add that I can't make any changes to how the XML is sent, I can only deal with the response.

Also, simply changing the attribute parameter to [XmlEnum("EnumValue ")] fixes the issue, but this is not satisfactory as the XML value could be altered at a later date.

If the XML isn't huge and/or performance isn't likely to be an issue, you could simply parse this with LINQ to XML and fix-up any values that aren't correct before deserializing using your XmlSerializer :

var doc = XDocument.Parse(xml);

foreach (var bar in doc.Descendants("Bar"))
{
    bar.Value = bar.Value.Trim();
}

using (var reader = doc.CreateReader())
{
    var obj = serializer.Deserialize(reader);
}

Please try this:

public class Foo
{
    [XmlIgnore]
    public MyEnum myEnum { get; set; }

    [XmlElement("Bar")]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public string _myEnum
    {
        get { return myEnum.ToString(); }
        set { myEnum = (MyEnum)Enum.Parse(typeof(MyEnum), value.Trim()); }
    }
}

Unfortunately there's no event that is fired on an unknown enum value that lets you substitute your own parsing algorithm. Also, XmlEnumAttribute doesn't set AttributeUsage.AllowMultiple = true so you can't add multiple enum aliases with various combinations of leading and trailing spaces.

One possible workaround would be to add a string-valued property to your class and do the parsing manually. Another would be to add a wrapper struct that encapsulates the enum to handle the parsing:

public class Foo
{
    [XmlIgnore]
    public MyEnum myEnum { get; set; }

    [XmlElement("Bar")]
    [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DebuggerBrowsable(DebuggerBrowsableState.Never)]
    public XmlEnumWrapper<MyEnum> myEnumXml { get { return myEnum; } set { myEnum = value; } }
}

public struct XmlEnumWrapper<TEnum> : IEquatable<XmlEnumWrapper<TEnum>> where TEnum : struct, IConvertible, IComparable, IFormattable
{
    TEnum value;

    public XmlEnumWrapper(TEnum value)
    {
        this.value = value;
    }

    public static implicit operator TEnum(XmlEnumWrapper<TEnum> wrapper)
    {
        return wrapper.Value;
    }

    public static implicit operator XmlEnumWrapper<TEnum>(TEnum anEnum)
    {
        return new XmlEnumWrapper<TEnum>(anEnum);
    }

    public static bool operator ==(XmlEnumWrapper<TEnum> first, XmlEnumWrapper<TEnum> second)
    {
        return first.Equals(second);
    }

    public static bool operator !=(XmlEnumWrapper<TEnum> first, XmlEnumWrapper<TEnum> second)
    {
        return !first.Equals(second);
    }

    [XmlIgnore]
    public TEnum Value { get { return value; } private set { this.value = value; } }

    [XmlText]
    public string ValueString
    {
        get
        {
            return Value.ToString();
        }
        set
        {
            // See here if one needs to parse XmlEnumAttribute attributes
            // http://stackoverflow.com/questions/3047125/retrieve-enum-value-based-on-xmlenumattribute-name-value
            value = value.Trim();
            Value = (TEnum)Enum.Parse(typeof(TEnum), value, false);
        }
    }

    #region IEquatable<XmlEnumWrapper<TEnum>> Members

    public bool Equals(XmlEnumWrapper<TEnum> other)
    {
        return EqualityComparer<TEnum>.Default.Equals(Value, other.Value);
    }

    #endregion

    public override bool Equals(object obj)
    {
        if (obj is XmlEnumWrapper<TEnum>)
            return Equals((XmlEnumWrapper<TEnum>)obj);
        return Value.Equals(obj);
    }

    public override int GetHashCode()
    {
        return Value.GetHashCode();
    }

    public override string ToString()
    {
        return Value.ToString();
    }
}

This also allows you to add synonym processing or handle unknown values without throwing an exception if it becomes necessary.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM