3

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.

AndyRN
  • 33
  • 4
  • You can use a custom deserializer to "cleanse" the data while deserializing. Either that or the terribly inefficient string.replace stripping all the spaces in the XML before deserializing (I probably would be hanged by the community for even suggesting this alternative). – Mrchief Jul 06 '15 at 18:00
  • 2
    Can you show the code you're using to deserialize? This might be as simple as adding in a .Trim() – oppassum Jul 06 '15 at 18:01
  • 1
    code please for getting more info – Pranay Rana Jul 06 '15 at 18:04
  • 1
    Sorry, I don't have the deserialize code to hand. Although, it's just a standard operation where the XML response is passed into the deserialize() method. Ignoring the inefficiency, stripping all of the spaces wouldn't work as most XML values need their spaces (i.e. bulks of text). – AndyRN Jul 06 '15 at 18:46

3 Answers3

2

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()); }
    }
}
Alexander Petrov
  • 13,457
  • 2
  • 20
  • 49
0

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);
}
Charles Mager
  • 25,735
  • 2
  • 35
  • 45
  • Unfortunately, performance is an issue. Also, I can't trim every value as most need the spaces that they are sent with (i.e. bulks of text). I'm aiming to be able to just trim values that are not expected to have spaces to avoid any errors being thrown down the line. – AndyRN Jul 06 '15 at 18:51
  • This won't trim every value, it only trims values of elements called `Bar`. You could use any query, any custom logic you like. As ever with performance, try various options and *measure*. – Charles Mager Jul 06 '15 at 18:53
  • Oh yes, my apologies, must have misread that. I may take this idea on board. Depends how well this can be implemented to rope in all the values that need to be checked. I'll let you know my progress tomorrow morning! – AndyRN Jul 06 '15 at 18:58
0

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.

dbc
  • 104,963
  • 20
  • 228
  • 340