2015-10-06 44 views
1

我有一個具有兩個枚舉成員的自定義類型。我試圖創建這個自定義類型的列表,然後將該列表序列化爲XML。當我嘗試做這件事時,Unity(我在Unity工作)崩潰,這在我之前從未做過。包含導致崩潰的System.Enum字段的類列表的XML序列化

我已經縮小問題到這個從我的序列化代碼:

xmls.Serialize(stream, dataCollection); 

所以當XmlSerializer的試圖序列名單的問題poccurs。我不知道爲什麼!所以任何幫助,將不勝感激。

下面的代碼。

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(); 
    } 
} 
+1

圍繞序列化的try/catch - 它寫入的異常是什麼?如果沒有任何異常在你的代碼的入口點附近放一個try/catch,看看它說了什麼 –

+0

剛剛嘗試在try catch中包裝問題行,它仍然崩潰Unity!很煩人。 –

+0

嘗試AppDomain.CurrentDomain.UnhandledException + =(s,e)=> Console.WriteLine(e); –

回答

2

您這裏有兩個不相關的問題:

  • 你有

    public override int GetHashCode() 
    { 
        return this.GetHashCode(); // Fix Me 
    } 
    

    無限遞歸顯然XmlSerializer使用的東西的哈希碼,所以你需要解決這個問題,這應該很容易。

  • 您試圖直接序列化System.Enum類型的對象System.Enum subType;。不幸的是,這種類型是抽象的,所以你不能直接序列化。您將需要向XML中添加更多信息,即序列化的enum類型。

我建議加入下面的包裝類這樣做是爲了封裝枚舉類型:

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

[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會是這個樣子:

<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> 

原型fiddle

+0

謝謝你。不能相信我錯過了GetHashCode()。我在element.Remove()行的SerializeToXElement擴展方法中收到錯誤:::: ** System.InvalidOperationException:生成XML文檔時發生錯誤。 ---> System.InvalidOperationException:父母丟失** –

+0

@MatthewSawrey - 我沒有得到.Net上的錯誤。你可以刪除'if(element!= null)元素.Remove();'邏輯,這是一個小的,可選的內存使用減少。 – dbc