1

我正在尝试在 Windows Phone 7 上序列化我的游戏状态,因此创建了一个“保存”结构,这样我就可以使用 DataContractSerializer 轻松地将所有状态转换为 XML 文档。

当我单独序列化 Gamestate 的每个数据成员时,Save() 代码运行良好。然而,这导致了多个 xml 根错误,因此我将“保存”类作为唯一根。现在,每当我尝试对任何东西调用 DataContractSerializer.WriteObject() 时,都会遇到安全异常。

我一直在比较我的旧代码和新代码,但找不到任何问题。
我已将所有代码粘贴在下面,以防问题出在 Save() 方法之外。
先看看 Save() 方法


异常详情:
在此处输入图像描述

堆栈跟踪:

   at System.Runtime.Serialization.DataContract.DataContractCriticalHelper.CreateDataContract(Int32 id, RuntimeTypeHandle typeHandle, Type type)
   at System.Runtime.Serialization.DataContract.DataContractCriticalHelper.GetDataContractSkipValidation(Int32 id, RuntimeTypeHandle typeHandle, Type type)
   at System.Runtime.Serialization.DataContract.GetDataContractSkipValidation(Int32 id, RuntimeTypeHandle typeHandle, Type type)
   at System.Runtime.Serialization.DataContract.GetDataContract(RuntimeTypeHandle typeHandle, Type type, SerializationMode mode)
   at System.Runtime.Serialization.DataContract.GetDataContract(RuntimeTypeHandle typeHandle, Type type)
   at System.Runtime.Serialization.DataContract.GetDataContract(Type type)
   at System.Runtime.Serialization.DataContractSerializer.get_RootContract()
   at System.Runtime.Serialization.DataContractSerializer.InternalWriteStartObject(XmlWriterDelegator writer, Object graph)
   at System.Runtime.Serialization.DataContractSerializer.InternalWriteObject(XmlWriterDelegator writer, Object graph)
   at System.Runtime.Serialization.XmlObjectSerializer.WriteObjectHandleExceptions(XmlWriterDelegator writer, Object graph)
   at System.Runtime.Serialization.XmlObjectSerializer.WriteObject(XmlDictionaryWriter writer, Object graph)
   at System.Runtime.Serialization.XmlObjectSerializer.WriteObject(Stream stream, Object graph)
   at GameState_test.GameState.Save(String filename)
   at GameState_test.MainPage.SaveButton_Click(Object sender, RoutedEventArgs e)
   at System.Windows.Controls.Primitives.ButtonBase.OnClick()
   at System.Windows.Controls.Button.OnClick()
   at System.Windows.Controls.Primitives.ButtonBase.OnMouseLeftButtonUp(MouseButtonEventArgs e)
   at System.Windows.Controls.Control.OnMouseLeftButtonUp(Control ctrl, EventArgs e)
   at MS.Internal.JoltHelper.FireEvent(IntPtr unmanagedObj, IntPtr unmanagedObjArgs, Int32 argsTypeIndex, Int32 actualArgsTypeIndex, String eventName)

新代码:

    namespace GameState_test
    {                                       //TODO: Find a way to not have this ignored
        [DataContract] public class Hazard { [IgnoreDataMember] public Planet CurrentPlanet;} 

        public class Connections
        {
            public Connections(List<List<int>> connections = null) { this.cons = connections; }

            public bool AreConnected(int Planet1, int Planet2)
            {
                for (int i = 0; i < cons[Planet1].Count; i++)
                    if (cons[Planet1][i] == Planet2) return true;

                return false;
            }

            public List<int> this [int index] { get { return cons[index]; } }

            internal readonly List<List<int>> cons; //internal so it can be read by serializer
        }

        [DataContract]
        public class GameState
        {
            public GameState(List<List<int>> connections) { this.Connections = new Connections(connections); }
            public GameState(Position pos, List<List<int>> con) { Position = pos; Connections = new Connections(con); }
            public GameState(string filename) //load a game
            {   
                using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication())
                {
                    if (!store.DirectoryExists("SavedGames")) store.CreateDirectory("SavedGames");

                    using (IsolatedStorageFileStream stream = store.OpenFile("SavedGames/" + filename + ".xml", System.IO.FileMode.Open, System.IO.FileAccess.Read))
                    {
                        DataContractSerializer serializer = new DataContractSerializer(typeof(GameStateSave), new List<Type> {typeof(Hazard), typeof(Inventory), typeof(List<List<int>>), typeof(List<Planet>), typeof(Planet) });
                        GameStateSave Save = (GameStateSave)serializer.ReadObject(stream);

                        Position position = new Position(Save.planets, Save.connections, Save.playerPosition);

                        this.Position = position;
                        this.PlayerInventory = Save.playerInventory;
                        this.Connections = new Connections(Save.connections);
                    }
                }
            }

            [DataMember] public readonly Connections Connections;
            [DataMember] public Position Position;
            [DataMember] public Inventory PlayerInventory;

            public void Save(string filename) 
            {
                using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication())
                {
                    if (!store.DirectoryExists("SavedGames")) store.CreateDirectory("SavedGames");

                    using (IsolatedStorageFileStream stream = store.OpenFile("SavedGames/" + filename + ".xml", System.IO.FileMode.OpenOrCreate, System.IO.FileAccess.Write))
                    {
                        DataContractSerializer serializer = new DataContractSerializer(typeof (GameStateSave), new List<Type> {typeof(Hazard), typeof(Inventory), typeof(List<List<int>>), typeof(List<Planet>), typeof(Planet)} );

                        GameStateSave Save = GenerateSave();
                        serializer.WriteObject(stream, Save);

                        //NOTE: Even when I comment everything out but this, I still get an exception:
                        //serializer.WriteObject(stream, this.Position.Player); 
                    }
                }
            }

            private GameStateSave GenerateSave()
            {
                GameStateSave Save = new GameStateSave();

                Save.connections = this.Connections.cons;
                Save.playerInventory = this.PlayerInventory;
                Save.planets = this.Position.Planets;
                Save.playerPosition = this.Position.Player;

                return Save;
            }

        }

        [DataContract]
        internal struct GameStateSave //only to be used here
        {
            [DataMember]
            public List<List<int>> connections;
            [DataMember]
            public Inventory playerInventory;
            [DataMember]
            public List<Planet> planets;
            [DataMember]
            public int playerPosition;
        }

    }

旧代码:

namespace GameState_test
{
    [DataContract] public class Hazard { [IgnoreDataMember] public Planet CurrentPlanet;}

    public class Connections
    {
        public Connections(List<List<int>> connections = null) { this.cons = connections; }

        public bool AreConnected(int Planet1, int Planet2)
        {
            for (int i = 0; i < cons[Planet1].Count; i++)
                if (cons[Planet1][i] == Planet2) return true;

            return false;
        }

        public List<int> this [int index] { get { return cons[index]; } }

        internal readonly List<List<int>> cons; //internal so it can be read by serializer
    }

    [DataContract]
    public class GameState
    {
        public GameState(List<List<int>> connections) { this.Connections = new Connections(connections); }
        public GameState(Position pos, List<List<int>> con) { Position = pos; Connections = new Connections(con); }
        public GameState(string filename) 
        {   //load a game
            using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication())
            {
                if (!store.DirectoryExists("SavedGames")) store.CreateDirectory("SavedGames");

                using (IsolatedStorageFileStream stream = store.OpenFile("SavedGames/" + filename + ".xml", System.IO.FileMode.Open, System.IO.FileAccess.Read))
                {
                    DataContractSerializer serializer = new DataContractSerializer(typeof(Hazard), new List<Type> { typeof(Inventory), typeof(List<List<int>>), typeof(List<Planet>), typeof(Planet) });

                    List<List<int>> Connections = (List<List<int>>) serializer.ReadObject(stream);
                    Inventory PlayerInventory = (Inventory) serializer.ReadObject(stream);
                    List<Planet> Planets = (List<Planet>) serializer.ReadObject(stream);
                    int PlayerPosition = (int)serializer.ReadObject(stream);

                    Position position = new Position(Planets, Connections, PlayerPosition);

                    this.Position = position;
                    this.PlayerInventory = PlayerInventory;
                    this.Connections = new Connections(Connections);
                }
            }
        }

        [DataMember] public readonly Connections Connections;
        [DataMember] public Position Position;
        [DataMember] public Inventory PlayerInventory;

        public void Save(string filename) 
        {
            using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication())
            {
                if (!store.DirectoryExists("SavedGames")) store.CreateDirectory("SavedGames");

                using (IsolatedStorageFileStream stream = store.OpenFile("SavedGames/" + filename + ".xml", System.IO.FileMode.OpenOrCreate, System.IO.FileAccess.Write))
                {
                    DataContractSerializer serializer = new DataContractSerializer(typeof (Hazard), new List<Type> {typeof(Inventory), typeof(List<List<int>>), typeof(List<Planet>), typeof(Planet)} );
                    serializer.WriteObject(stream, this.Connections.cons);
                    serializer.WriteObject(stream, this.PlayerInventory);
                    serializer.WriteObject(stream, this.Position.Planets);
                    serializer.WriteObject(stream, this.Position.Player);
                }
            }
        }

        internal class SerializableGameState //only to be used here
        {
            public List<List<int>> connections;
            public Inventory playerInventory;
            public List<Planet> planets;
            public int playerPosition;
        }

    }
}
4

3 回答 3

2

Silverlight 代码在部分信任的基础上运行,这意味着它不能完全访问系统中的对象,包括非公共类型和成员。您正在尝试序列化一个内部类,该类不能按原样工作。这是我尝试序列化内部类时遇到的异常:

Exception: System.Security.SecurityException: The data contract type 'SL_ButtonAndText.SerializableGameState' is not serializable because it is not public. Making the type public will fix this error. Alternatively, you can make it internal, and use the InternalsVisibleToAttribute attribute on your assembly in order to enable serialization of internal members - see documentation for more details. Be aware that doing so has certain security implications. ---> System.Security.SecurityException: Security error.
   at System.Runtime.Serialization.CodeGenerator.BeginMethod(Type returnType, String methodName, Type[] argTypes, Boolean allowPrivateMemberAccess)
   at System.Runtime.Serialization.CodeGenerator.BeginMethod(String methodName, Type delegateType, Boolean allowPrivateMemberAccess)
   at System.Runtime.Serialization.XmlFormatWriterGenerator.CriticalHelper.GenerateClassWriter(ClassDataContract classContract)
   --- End of inner exception stack trace ---
   at System.Runtime.Serialization.ClassDataContract.RequiresMemberAccessForWrite(SecurityException securityException, String[] serializationAssemblyPatterns)
   at System.Runtime.Serialization.XmlFormatWriterGenerator.CriticalHelper.GenerateClassWriter(ClassDataContract classContract)
   at System.Runtime.Serialization.ClassDataContract.get_XmlFormatWriterDelegate()
   at System.Runtime.Serialization.ClassDataContract.WriteXmlValue(XmlWriterDelegator xmlWriter, Object obj, XmlObjectSerializerWriteContext context)
   at System.Runtime.Serialization.XmlObjectSerializerWriteContext.WriteDataContractValue(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle declaredTypeHandle)
   at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeWithoutXsiType(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle declaredTypeHandle)
   at System.Runtime.Serialization.DataContractSerializer.InternalWriteObjectContent(XmlWriterDelegator writer, Object graph)
   at System.Runtime.Serialization.DataContractSerializer.InternalWriteObject(XmlWriterDelegator writer, Object graph)
   at System.Runtime.Serialization.XmlObjectSerializer.WriteObjectHandleExceptions(XmlWriterDelegator writer, Object graph)
   at System.Runtime.Serialization.XmlObjectSerializer.WriteObject(XmlDictionaryWriter writer, Object graph)
   at System.Runtime.Serialization.XmlObjectSerializer.WriteObject(Stream stream, Object graph)
   at SL_ButtonAndText.MainPage.Button_Click(Object sender, RoutedEventArgs e)

异常消息准确地说明了我的问题(您的异常消息是否相似?)。解决此问题的一种方法是将课程公开,这将是最简单的解决方案。如错误消息所示,另一种选择是通过使用属性显式允许DataContractSerializer对内部类型具有可见性。[InternalsVisibleTo]下面的代码具有该[IVT]属性,并补充说我不再看到该错误。

using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
using System.Text;
using System.Windows;
using System.Windows.Controls;

[assembly: InternalsVisibleTo("System.Runtime.Serialization")]

namespace SL_ButtonAndText
{
    public partial class MainPage : UserControl
    {
        public MainPage()
        {
            InitializeComponent();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            AddToDebug(typeof(DataContractSerializer).Assembly.FullName);
            MemoryStream ms = new MemoryStream();
            DataContractSerializer dcs = new DataContractSerializer(typeof(SerializableGameState));
            SerializableGameState sgs = new SerializableGameState
            {
                connections = new List<List<int>>
                {
                    new List<int> { 1, 2, 3 },
                    new List<int> { 4, 5 },
                },
                playerPosition = 0
            };
            try
            {
                dcs.WriteObject(ms, sgs);
                AddToDebug("Serialized: {0}", Encoding.UTF8.GetString(ms.GetBuffer(), 0, (int)ms.Position));
            }
            catch (Exception ex)
            {
                AddToDebug("Exception: {0}", ex);
            }
        }

        private void AddToDebug(string text, params object[] args)
        {
            if (args != null && args.Length > 0) text = string.Format(text, args);
            this.Dispatcher.BeginInvoke(() => this.txtDebug.Text = this.txtDebug.Text + text + Environment.NewLine);
        }
    }

    [DataContract]
    internal class SerializableGameState
    {
        [DataMember]
        public List<List<int>> connections;
        [DataMember]
        public int playerPosition;
    }
}
于 2012-05-10T20:22:27.897 回答
1

Carlosfigueira 是对的,因为在 Silverlight 中看不到内部结构。要修复它,请在 AssemblyInfo.cs 中添加以下内容:

[assembly: InternalsVisibleTo("System.Runtime.Serialization, PublicKey="
+ "00240000048000009400000006020000002400005253413100040000010001008D56C76F9E86493"
+ "83049F383C44BE0EC204181822A6C31CF5EB7EF486944D032188EA1D3920763712CCB12D75FB77E"
+ "9811149E6148E5D32FBAAB37611C1878DDC19E20EF135D0CB2CFF2BFEC3D115810C3D9069638FE4"
+ "BE215DBF795861920E5AB6F7DB2E2CEEF136AC23D5DD2BF031700AEC232F6C6B1C785B4305C123B"
+ "37AB")]
[assembly: InternalsVisibleTo("System.ServiceModel.Web, PublicKey="
+ "00240000048000009400000006020000002400005253413100040000010001008D56C76F9E86493"
+ "83049F383C44BE0EC204181822A6C31CF5EB7EF486944D032188EA1D3920763712CCB12D75FB77E"
+ "9811149E6148E5D32FBAAB37611C1878DDC19E20EF135D0CB2CFF2BFEC3D115810C3D9069638FE4"
+ "BE215DBF795861920E5AB6F7DB2E2CEEF136AC23D5DD2BF031700AEC232F6C6B1C785B4305C123B"
+ "37AB")]
[assembly: InternalsVisibleTo("System.Runtime.Serialization.Json, PublicKey="
+ "00240000048000009400000006020000002400005253413100040000010001008D56C76F9E86493"
+ "83049F383C44BE0EC204181822A6C31CF5EB7EF486944D032188EA1D3920763712CCB12D75FB77E"
+ "9811149E6148E5D32FBAAB37611C1878DDC19E20EF135D0CB2CFF2BFEC3D115810C3D9069638FE4"
+ "BE215DBF795861920E5AB6F7DB2E2CEEF136AC23D5DD2BF031700AEC232F6C6B1C785B4305C123B"
+ "37AB")]

来源:http ://systemmetaphor.blogspot.fr/2010/04/silverlight-serialization-avoiding.html

于 2014-03-31T07:50:40.817 回答
0

检查您正在保存或读取的文件的权限。也许是简单的文件权限错误。检查文件夹权限和管理权限等。

于 2012-05-10T06:26:17.880 回答