我正在尝试在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;
}
}
}
答案 0 :(得分: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;
}
}
答案 1 :(得分: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
答案 2 :(得分:0)
检查您要保存或读取的文件的权限。也许这是简单的文件权限错误。检查文件夹权限和管理权限等。