使用 DataContractSerializer 进行序列化时出现 SecurityException [英] SecurityException when serializing with DataContractSerializer

查看:31
本文介绍了使用 DataContractSerializer 进行序列化时出现 SecurityException的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm trying to serialize the state of my game on Windows Phone 7, and so made a "Save" struct so I could easily convert all the states into an XML doc using DataContractSerializer.

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

The Save() code worked fine when I just serialized each data member of the Gamestate individually. However this led to a multiple xml root error, so I made the "Save" class to act as the sole root. Now I get a security exception whenever I try and call DataContractSerializer.WriteObject() on anything.

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

I've been comparing my old and new code but just can't find anything wrong.
I've pasted all my code below in case the problem lies outside the Save() method.
Just look at the Save() method first since

异常详情:

堆栈跟踪:

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

    }
}

推荐答案

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

Silverlight code runs on partial trust, which means that it doesn't have full access to objects in the system, including non-public types and members. You're trying to serialize an internal class, which doesn't work as is. Here's the exception which I get when I try to serialize an internal class:

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)

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

The exception message tells exactly what my problem is (is your exception message similar?). One way to solve this would be to make the class public, which would be the simplest solution. Another alternative, as indicated by the error message, would be to explicitly allow the DataContractSerializer to have visibility to the internal types, by using the [InternalsVisibleTo] attribute. The code below has the [IVT] attribute, and adding that I don't see the error anymore.

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

这篇关于使用 DataContractSerializer 进行序列化时出现 SecurityException的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆