对带有空格的标记枚举进行反序列化会导致SerializationException

基斯·迪克

当对用EnumMemberAttribute装饰且带有包含空格的值的标记枚举进行反序列化时,将引发SerializationException。值中的空格被视为分隔符。

有没有办法更改分隔符或将值放在引号中?还是有一个更简单的解决方案?

我已经在考虑的选项是:

  • 用此枚举类型的列表替换标记的枚举
  • 用下划线代替空格
  • 这是在WCF服务中使用的,并且我知道某些数据合同中的枚举被认为是一件坏事。因此,我也正在考虑失去所有枚举。

但是我真的觉得这应该是可配置的,或者其他人已经解决的。但是我什么也找不到。

我把问题归结为一个简单的单元测试。以下代码导致:

消息=无效的枚举值' Test '不能反序列化为'UnitTests.TestEnum'类型。如果类型具有DataContractAttribute属性,请确保存在必要的枚举值并用EnumMemberAttribute属性标记。源= System.Runtime.Serialization

using System;
using System.IO;
using System.Runtime.Serialization;
using System.Xml;
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace UnitTests
{
    [TestClass]
    public class EnumSerizalizationTests
    {
        [TestMethod]
        public void SerializingAndDesrializingAFlaggedEnumShouldResultInSameEnumValues()
        {
            //Arrange
            var orgObject = new TestClass { Value = TestEnum.TestValue1 | TestEnum.TestValue2 };
            //Act
            var temp = DataContractSerializeObject(orgObject);
            var newObject = DataContractDeSerializeObject<TestClass>(temp);

            //Assert
            newObject.ShouldBeEquivalentTo(orgObject, "Roundtripping serialization should result in same value");
        }

        public string DataContractSerializeObject<T>(T objectToSerialize)
        {
            using (var output = new StringWriter())
            {
                using (var writer = new XmlTextWriter(output) {Formatting = Formatting.Indented})
                {
                    new DataContractSerializer(typeof (T)).WriteObject(writer, objectToSerialize);
                    return output.GetStringBuilder().ToString();
                }
            }
        }

        public T DataContractDeSerializeObject<T>(string stringToDeSerialize)
        {
            DataContractSerializer ser = new DataContractSerializer(typeof(T));
            T result;
            using (StringReader stringReader = new StringReader(stringToDeSerialize))
            {
                using (XmlReader xmlReader = XmlReader.Create(stringReader))
                {
                    result = (T)ser.ReadObject(xmlReader);
                }
            }
            return result;
        }

    }

    [DataContract]
    [KnownType(typeof(TestEnum))]
    public class TestClass
    {
        [DataMember]
        public TestEnum Value { get; set; }
    }

    [Flags]
    [DataContract]
    public enum TestEnum
    {
        [EnumMember(Value = "Test value one")]
        TestValue1 = 1,
        [EnumMember(Value = "Test value two")]
        TestValue2 = 2,
        [EnumMember]
        TestValue3 = 4,
        [EnumMember]
        TestValue4 = 8,
    }


}
Yoh Deadfall

您不能在值中使用空格,因为使用空格DataContractSerializer并且它是硬编码的。请参阅帖子但是,如果您真的想在单词之间使用空格,请使用以下列出的解决方案之一:

第一种方式。在值中使用其他空格字符,例如每em三个空格。但是您会有另一个问题-值之间没有视觉分隔符。

<TestClass xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/ConsoleApplication">
  <Value>Test value one Test value two</Value>
</TestClass>

第二种方法是使用IDataContractSurrogate这种方式将产生下面列出的XML:

<TestClass xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/ConsoleApplication">
  <Value i:type="EnumValueOfTestEnum9cBcd6LT">Test value one, Test value two</Value>
</TestClass>

它是如何工作的?我们将只在序列化过程中包装枚举,并在反序列化的情况下解开包装。为此,我们应该使用IDataContractSurrogate

new DataContractSerializerSettings()
{
    DataContractSurrogate = new EnumSurrogate(),
    KnownTypes = new Type[] { typeof(EnumValue<TestEnum>) }
};

public class EnumSurrogate : IDataContractSurrogate
{
    #region IDataContractSurrogate Members

    public object GetCustomDataToExport(Type clrType, Type dataContractType)
    {
        return null;
    }

    public object GetCustomDataToExport(MemberInfo memberInfo, Type dataContractType)
    {
        return null;
    }

    public Type GetDataContractType(Type type)
    {
        return type;
    }

    public object GetDeserializedObject(object obj, Type targetType)
    {
        IEnumValue enumValue = obj as IEnumValue;

        if (enumValue!= null)
        { return enumValue.Value; }

        return obj;
    }

    public void GetKnownCustomDataTypes(Collection<Type> customDataTypes)
    {
    }

    public object GetObjectToSerialize(object obj, Type targetType)
    {
        if (obj != null)
        {
            Type type = obj.GetType();

            if (type.IsEnum && Attribute.IsDefined(type, typeof(FlagsAttribute)))
            { return Activator.CreateInstance(typeof(EnumValue<>).MakeGenericType(type), obj); }
        }

        return obj;
    }

    public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData)
    {
        return null;
    }

    public CodeTypeDeclaration ProcessImportedType(CodeTypeDeclaration typeDeclaration, CodeCompileUnit compileUnit)
    {
        return null;
    }

    #endregion
}

public interface IEnumValue : IXmlSerializable
{
    object Value { get; }
}

[Serializable]
public class EnumValue<T> : IEnumValue
    where T : struct
{
    #region Fields

    private Enum value;
    private static Type enumType;
    private static long[] values;
    private static string[] names;
    private static bool isULong;

    #endregion

    #region Constructors

    static EnumValue()
    {
        enumType = typeof(T);

        if (!enumType.IsEnum)
        { throw new InvalidOperationException(); }

        FieldInfo[] fieldInfos = enumType.GetFields(BindingFlags.Static | BindingFlags.Public);

        values = new long[fieldInfos.Length];
        names = new string[fieldInfos.Length];
        isULong = Enum.GetUnderlyingType(enumType) == typeof(ulong);

        for (int i = 0; i < fieldInfos.Length; i++)
        {
            FieldInfo fieldInfo = fieldInfos[i];
            EnumMemberAttribute enumMemberAttribute = (EnumMemberAttribute)fieldInfo
                .GetCustomAttributes(typeof(EnumMemberAttribute), false)
                .FirstOrDefault();
            IConvertible value = (IConvertible)fieldInfo.GetValue(null);

            values[i] = (isULong)
                ? (long)value.ToUInt64(null)
                : value.ToInt64(null);
            names[i] = (enumMemberAttribute == null || string.IsNullOrEmpty(enumMemberAttribute.Value))
                ? fieldInfo.Name
                : enumMemberAttribute.Value;
        }
    }

    public EnumValue()
    {
    }

    public EnumValue(Enum value)
    {
        this.value = value;
    }

    #endregion

    #region IXmlSerializable Members

    public XmlSchema GetSchema()
    {
        return null;
    }

    public void ReadXml(XmlReader reader)
    {
        string stringValue = reader.ReadElementContentAsString();

        long longValue = 0;
        int i = 0;

        // Skip initial spaces
        for (; i < stringValue.Length && stringValue[i] == ' '; i++) ;

        // Read comma-delimited values
        int startIndex = i;
        int nonSpaceIndex = i;
        int count = 0;

        for (; i < stringValue.Length; i++)
        {
            if (stringValue[i] == ',')
            {
                count = nonSpaceIndex - startIndex + 1;

                if (count > 1)
                { longValue |= ReadEnumValue(stringValue, startIndex, count); }

                nonSpaceIndex = ++i;

                // Skip spaces
                for (; i < stringValue.Length && stringValue[i] == ' '; i++) ;

                startIndex = i;

                if (i == stringValue.Length)
                { break; }
            }
            else
            {
                if (stringValue[i] != ' ')
                { nonSpaceIndex = i; }
            }
        }

        count = nonSpaceIndex - startIndex + 1;

        if (count > 1)
            longValue |= ReadEnumValue(stringValue, startIndex, count);

        value = (isULong)
            ? (Enum)Enum.ToObject(enumType, (ulong)longValue)
            : (Enum)Enum.ToObject(enumType, longValue);
    }

    public void WriteXml(XmlWriter writer)
    {
        long longValue = (isULong)
            ? (long)((IConvertible)value).ToUInt64(null)
            : ((IConvertible)value).ToInt64(null);

        int zeroIndex = -1;
        bool noneWritten = true;

        for (int i = 0; i < values.Length; i++)
        {
            long current = values[i];

            if (current == 0)
            {
                zeroIndex = i;
                continue;
            }

            if (longValue == 0)
            { break; }

            if ((current & longValue) == current)
            {
                if (noneWritten)
                { noneWritten = false; }
                else
                { writer.WriteString(","); }

                writer.WriteString(names[i]);
                longValue &= ~current;
            }
        }

        if (longValue != 0)
        { throw new InvalidOperationException(); }

        if (noneWritten && zeroIndex >= 0)
        { writer.WriteString(names[zeroIndex]); }
    }

    #endregion

    #region IEnumValue Members

    public object Value
    {
        get { return value; }
    }

    #endregion

    #region Private Methods

    private static long ReadEnumValue(string value, int index, int count)
    {
        for (int i = 0; i < names.Length; i++)
        {
            string name = names[i];

            if (count == name.Length && string.CompareOrdinal(value, index, name, 0, count) == 0)
            { return values[i]; }
        }

        throw new InvalidOperationException();
    }

    #endregion
}

第三种方法是动态发出类,如果基类具有标记的Enum属性,则将其替换为string属性,并将生成的类的实例用作替代。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

带有空格的反序列化 JSON 响应 C# 类

带有枚举类型的JSON反序列化

将Json反序列化为带有包含空格的枚举的NJsonSchema生成的对象

枚举序数会影响BinaryFormatter反序列化吗?

如何使用 serde 序列化/反序列化带有包含数据的枚举键的 HashMap?

Tastypie反序列化会导致{“ error”:“”}

对具有通用类型的可选struct字段反序列化会导致语义怪异

反序列化XML具有空对象

.NET Core 3.1中使用System.Text.Json进行反序列化会导致空属性值

为什么类型会导致“带有动物可序列化的产品”而不是动物?

反序列化后如何处理SerializationException

SerializationException:反序列化Avro消息时出错(StringIndexOutOfBoundsException)

SerializationException:反序列化 Avro 消息时出错

Java枚举字段序列化/反序列化

是否可以序列化/反序列化枚举?

Java中枚举的反序列化

从JSON反序列化Java枚举

用Jackson反序列化枚举

无法反序列化JSON以枚举

从Serializationinfo存储反序列化枚举?

反序列化枚举C#

带有RedissonSessionManager的Tomcat无法反序列化SecurityContextImpl

带有根元素的Jackson JSON反序列化

带有 ParameterNamesModule 的 Jackson 无法反序列化类

带有额外变量的 gson 数组反序列化

JPA 检索带有空格的枚举

参照现有对象与Jackson进行反序列化

将带有中文字符的XML发布到Microsoft Translator API会引发反序列化异常

包含ArrayList的反序列化对象具有所有空值