gpt4 book ai didi

c# - 使用空格反序列化标记的枚举会导致 SerializationException

转载 作者:太空狗 更新时间:2023-10-29 17:54:55 24 4
gpt4 key购买 nike

当反序列化带有 EnumMemberAttribute 且值包含空格的标记枚举时,将抛出 SerializationException。值中的空格被视为分隔符。

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

我已经在考虑的选项是:

  • 用这个枚举类型的列表替换标记的枚举
  • 用下划线替换空格
  • 这是在 WCF 服务中使用的,我是意识到某些人认为数据契约(Contract)中的枚举是一件坏事。所以我也在考虑一起失去枚举。

但我真的觉得这应该是可配置的东西或者其他人已经解决的东西。但我找不到任何东西。

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

Message=Invalid enum value 'Test' cannot be deserialized into type 'UnitTests.TestEnum'. Ensure that the necessary enum values are present and are marked with EnumMemberAttribute attribute if the type has DataContractAttribute attribute. Source=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,
}


}

最佳答案

您不能在值中使用空格,因为 DataContractSerializer 使用它并且它是硬编码的。查看sourcepost .但是如果你真的想在单词之间使用空格,那么使用列出的解决方案之一:

第一种方式。在值中使用其他空白字符,例如每 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 属性并使用实例生成类的代理。

关于c# - 使用空格反序列化标记的枚举会导致 SerializationException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28898850/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com