包含System.Enum字段的类列表的XML序列化导致崩溃 [英] XML serialization of list of class containing a System.Enum field causing crash
问题描述
我有一个带有两个枚举成员的自定义类型。我正在尝试创建此自定义类型的列表,然后将该列表序列化为XML。当我尝试这样做时,Unity(我在Unity上工作)崩溃了,这是我以前从未做过的。
I have a custom type with two enum members. I'm trying to create a list of this custom type, then serialize that list to XML. When I try and do this, Unity (I'm working in Unity) crashes, which it's never done on me before.
我已将问题缩小到序列化代码中:
I've narrowed the issue down to this from my serialization code:
xmls.Serialize(stream, dataCollection);
因此,当XMLSerializer尝试序列化我的List时,就会出现此问题。我不知道为什么!因此,任何帮助将不胜感激。
So the issue poccurs when the XMLSerializer attempts to serialize my List. I have no idea why though! So any help would be appreciated.
下面的代码。
XML序列化代码
public static void WriteGenericCollectionToXML<T>(T dataCollection, string filePath) where T : IEnumerable, ICollection
{
//Check to see if file already exists
if(!File.Exists(filePath))
{
//if not, create it
File.Create(filePath);
}
try
{
XmlSerializer xmls = new XmlSerializer(typeof(T));
using(Stream stream = new FileStream(filePath, FileMode.Append))
{
xmls.Serialize(stream, dataCollection);
}
}
catch(Exception e)
{
Debug.LogException(e);
}
}
创建我的对象列表的代码自定义类型
public List<BlockType> t = new List<BlockType>();
t.Add(new BlockType(true));
t.Add(new BlockType(true));
t.Add(new BlockType(true));
SaveLoad.WriteGenericCollectionToXML(t, Application.dataPath + "/test.xml");
自定义类型
using UnityEngine;
using System.Collections;
using System.Xml;
using System.Xml.Serialization;
public enum BaseBlockType
{
[XmlEnum(Name = "Animals")]Animals,
[XmlEnum(Name = "Geometry")]Geometry,
[XmlEnum(Name = "Letters")]Letters
}
public enum LetterBlockType
{
[XmlEnum(Name = "A")]A,
[XmlEnum(Name = "B")]B,
[XmlEnum(Name = "C")]C,
[XmlEnum(Name = "D")]Z,
[XmlEnum(Name = "E")]X,
[XmlEnum(Name = "F")]F,
[XmlEnum(Name = "G")]G
}
public enum AnimalType
{
[XmlEnum(Name = "Elephant")]Elephant,
[XmlEnum(Name = "Giraffe")]Giraffe,
[XmlEnum(Name = "Tiger")]Tiger,
[XmlEnum(Name = "Sheep")]Sheep
}
public enum GeometaryType
{
[XmlEnum(Name = "Square")]Square,
[XmlEnum(Name = "Triangle")]Triangle,
[XmlEnum(Name = "Circle")]Circle,
[XmlEnum(Name = "Star")]Star
}
[XmlType]
public class BlockType
{
[XmlAttribute]
public BaseBlockType baseType;
[XmlAttribute]
public System.Enum subType;
public BlockType(BaseBlockType baseT, System.Enum subT)
{
//Set base type
baseType = baseT;
//Set sub type
subType = subT;
//possibly need to perform checks that sub-type is acceptable
}
public BlockType(BaseBlockType baseT)
{
//Set base type
baseType = baseT;
//Set sub type
subType = RandSubType(baseType);
}
public BlockType(bool random)
{
if(random)
{
//Set base type
int enumLength = System.Enum.GetValues(typeof(BaseBlockType)).Length;
int rand = Random.Range(0, enumLength);
baseType = (BaseBlockType)rand;
//Set sub type
subType = RandSubType(baseType);
}
}
public BlockType()
{}
public System.Enum RandSubType(BaseBlockType baseType)
{
int subEnumLength;
int subRand;
switch(baseType)
{
case BaseBlockType.Animals:
subEnumLength = System.Enum.GetValues(typeof(AnimalType)).Length;
subRand = Random.Range(0, subEnumLength);
return (AnimalType)subRand;
case BaseBlockType.Geometry:
subEnumLength = System.Enum.GetValues(typeof(GeometaryType)).Length;
subRand = Random.Range(0, subEnumLength);
return(GeometaryType)subRand;
case BaseBlockType.Letters:
subEnumLength = System.Enum.GetValues(typeof(LetterBlockType)).Length;
subRand = Random.Range(0, subEnumLength);
return (LetterBlockType)subRand;
default:
Debug.Log("Block Sub Type Selection not working");
return null;
}
}
public override string ToString ()
{
string result = baseType.ToString() + "." + subType.ToString();
return result;
}
public override bool Equals (object t)
{
var test = t as BlockType;
if(t == null)
return false;
return(test.baseType == this.baseType && test.subType == this.subType);
}
public override int GetHashCode ()
{
return this.GetHashCode ();
}
}
推荐答案
您这里有两个不相关的问题:
You have two unrelated problems here:
-
您在
You have an infinite recursion in
public override int GetHashCode()
{
return this.GetHashCode(); // Fix Me
}
显然 XmlSerializer
使用哈希码处理某些事情,因此您需要解决此问题,这应该很容易。
Apparently XmlSerializer
uses the hash code for something, so you need to fix this, which should be easy.
您正尝试直接序列化对象 System.Enum子类型;
类型为 System.Enum
。不幸的是,这种类型是抽象的,因此您不能直接对其进行序列化。您将需要向XML添加更多信息,即要序列化的 enum
类型。
You are trying to directly serialize an object System.Enum subType;
of type System.Enum
. Unfortunately, this type is abstract, so you can't serialize this directly. You're going to need to add more information to your XML, namely the enum
type being serialized.
我建议通过添加以下包装器类来封装枚举类型来做到这一点:
I would suggest doing this by adding the following wrapper class to encapsulate the enum type:
public sealed class XmlEnumWrapper
{
System.Enum value;
Type type;
[XmlAttribute("enumType")]
public string XmlEnumType
{
get
{
if (Type == null)
return null;
return Type.AssemblyQualifiedName;
}
set
{
if (String.IsNullOrWhiteSpace(value))
{
Type = null;
}
else
{
Type = Type.GetType(value);
}
}
}
public abstract class EnumWraperBase
{
public abstract System.Enum BaseValue { get; }
}
[XmlRoot("Wrapper")]
public sealed class InnerEnumWraper<T> : EnumWraperBase
{
public InnerEnumWraper() { }
public InnerEnumWraper(T value) { this.Value = value; }
public T Value { get; set; }
public override Enum BaseValue { get { return (System.Enum)(object)Value; } }
}
[XmlText]
public string XmlValue
{
get
{
if (Value == null)
return null;
var wrapper = Activator.CreateInstance(typeof(InnerEnumWraper<>).MakeGenericType(Type), new object[] { Value });
// Handle [XmlEnum(Name = "XXX")] attributes applied to enum values by making a nested call to XmlSerializer
return (string)wrapper.SerializeToXElement().Element("Value");
}
set
{
if (String.IsNullOrWhiteSpace(value))
{
Value = null;
}
else if (Type == null)
{
throw new InvalidOperationException("Type was not set");
}
else
{
var xelement = new XElement("Wrapper", new XElement("Value", value.Trim()));
var wrapper = (EnumWraperBase)xelement.Deserialize(typeof(InnerEnumWraper<>).MakeGenericType(Type));
Value = (wrapper == null ? null : wrapper.BaseValue);
}
}
}
[XmlIgnore]
public System.Enum Value
{
get
{
return value;
}
set
{
this.value = value;
if (value != null)
type = value.GetType();
}
}
[XmlIgnore]
public Type Type
{
get
{
return type;
}
set
{
if (value != null)
{
if (!value.IsEnum || value.IsAbstract)
throw new ArgumentException();
}
this.type = value;
if (this.value != null && this.type != this.value.GetType())
this.value = null;
}
}
public override string ToString()
{
return value == null ? "" : value.ToString();
}
}
public static class XmlExtensions
{
public static T LoadFromXML<T>(this string xmlString)
{
T returnValue = default(T);
using (StringReader reader = new StringReader(xmlString))
{
object result = new XmlSerializer(typeof(T)).Deserialize(reader);
if (result is T)
{
returnValue = (T)result;
}
}
return returnValue;
}
public static string GetXml(this object obj)
{
using (var textWriter = new StringWriter())
{
var settings = new XmlWriterSettings() { Indent = true, IndentChars = " " }; // For cosmetic purposes.
using (var xmlWriter = XmlWriter.Create(textWriter, settings))
new XmlSerializer(obj.GetType()).Serialize(xmlWriter, obj);
return textWriter.ToString();
}
}
public static object Deserialize(this XContainer element, Type type)
{
using (var reader = element.CreateReader())
{
return new XmlSerializer(type).Deserialize(reader);
}
}
public static XElement SerializeToXElement<T>(this T obj)
{
var doc = new XDocument();
using (var writer = doc.CreateWriter())
new XmlSerializer(obj.GetType()).Serialize(writer, obj);
var element = doc.Root;
if (element != null)
element.Remove();
return element;
}
}
然后,按如下所示修改您的类,修复无穷大递归,忽略 subType
字段,而是将 public XmlEnumWrapper XmlSubTypeWrapper
属性序列化为 Element
:
Then, modify your class as follows, fixing the infinite recursion, ignoring the subType
field, and serializing instead a public XmlEnumWrapper XmlSubTypeWrapper
property as an Element
:
[XmlType]
public class BlockType
{
[XmlAttribute]
public BaseBlockType baseType;
[XmlIgnore]
public System.Enum subType;
[XmlElement("subType")]
public XmlEnumWrapper XmlSubTypeWrapper
{
get
{
return (subType == null ? null : new XmlEnumWrapper { Value = subType });
}
set
{
subType = (value == null ? null : value.Value);
}
}
public BlockType(BaseBlockType baseT, System.Enum subT)
{
//Set base type
baseType = baseT;
//Set sub type
subType = subT;
//possibly need to perform checks that sub-type is acceptable
}
public BlockType(BaseBlockType baseT)
{
//Set base type
baseType = baseT;
//Set sub type
subType = RandSubType(baseType);
}
public BlockType(bool random)
{
if (random)
{
//Set base type
int enumLength = System.Enum.GetValues(typeof(BaseBlockType)).Length;
int rand = Random.Range(0, enumLength);
baseType = (BaseBlockType)rand;
//Set sub type
subType = RandSubType(baseType);
}
}
public BlockType()
{ }
public System.Enum RandSubType(BaseBlockType baseType)
{
int subEnumLength;
int subRand;
switch (baseType)
{
case BaseBlockType.Animals:
subEnumLength = System.Enum.GetValues(typeof(AnimalType)).Length;
subRand = Random.Range(0, subEnumLength);
return (AnimalType)subRand;
case BaseBlockType.Geometry:
subEnumLength = System.Enum.GetValues(typeof(GeometaryType)).Length;
subRand = Random.Range(0, subEnumLength);
return (GeometaryType)subRand;
case BaseBlockType.Letters:
subEnumLength = System.Enum.GetValues(typeof(LetterBlockType)).Length;
subRand = Random.Range(0, subEnumLength);
return (LetterBlockType)subRand;
default:
Debug.WriteLine("Block Sub Type Selection not working");
return null;
}
}
public override string ToString()
{
string result = baseType.ToString() + "." + subType.ToString();
return result;
}
public override bool Equals(object t)
{
var test = t as BlockType;
if (t == null)
return false;
return (test.baseType == this.baseType && test.subType == this.subType);
}
public override int GetHashCode()
{
var code1 = baseType.GetHashCode();
var code2 = subType == null ? 0 : subType.GetHashCode();
return unchecked(~code1 ^ (7 + code2 << 3));
}
}
这样做,您的XML现在看起来像:
Having done so, your XML will now look like:
<ArrayOfBlockType xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<BlockType baseType="Letters">
<subType enumType="LetterBlockType, euj3revx, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null">LetterBlockTypeC</subType>
</BlockType>
<BlockType baseType="Geometry">
<subType enumType="GeometaryType, euj3revx, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null">Triangle</subType>
</BlockType>
<BlockType baseType="Animals">
<subType enumType="AnimalType, euj3revx, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null">Tiger</subType>
</BlockType>
</ArrayOfBlockType>
原型小提琴。
这篇关于包含System.Enum字段的类列表的XML序列化导致崩溃的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!