有人能推荐一种方法来检查一个类是否可以被序列化为XML吗?

6
我有一个泛型类,它接受类型为T的对象,将其序列化为XML,然后保存到文件系统。如果对象不可序列化,则序列化操作会失败。虽然这本身不是问题,但我认为最好在我的类构造函数中检查T的实例是否可序列化,如果不可序列化,则在那个时候抛出错误而不是以后。
除了尝试在TRY...CATCH中实例化并序列化对象之外,是否有一种方法来检查T的实例是否可以作为XML序列化?如果我能以某种方式查询类T以发现它是否可以作为XML序列化,那就太好了。
如果有帮助的话,可以在此处查看代码:http://winrtstoragehelper.codeplex.com/SourceControl/changeset/view/ac24e6e923cd#WinRtUtility%2fWinRtUtility%2fObjectStorageHelper.cs 请注意,此代码针对WinRT进行编译(即用于Windows 8应用程序),但我认为该问题与任何C#方言相关。
谢谢。

如果您只是想检查类是否使用SerializableAttribte进行了装饰,请使用typeof(T)。GetCustomAttributes(typeof(SerializableAttribute),false) - LeffeBrune
嗨LeffeBrune,感谢回复。不,我不仅仅想检查它是否具有该属性。我的评论希望解释为什么不这样做。 - jamiet
2个回答

4
据我所知,即使您检查各种属性(如SerializableDataContract),或者检查Type.IsSerializable(我认为这只是一个检查Serializable属性是否存在的便捷方法),也不能保证实现实际上是可序列化的。(编辑:正如提问中提供的示例代码所示,XmlSerializer不依赖于Serializable属性修饰。因此,检查这些标志是没有意义的。) 根据我的经验,最好的方法是使用单元测试来验证应用程序中使用的各种类型,并使用try/catch来查看它是否通过/失败。在运行时,使用try/catch(而不是每次预先检查)并记录/处理异常。
如果您有一个有效兼容类型列表作为单元测试的结果,可以对编译时从先前测试中确定的列表中的T进行预检查,并假设任何其他类型都是无用的。但要注意已知有效类型的子类,因为即使它们继承自有效的可序列化类型,它们的实现也可能不是。
编辑:由于这是针对Windows Phone 8的,虽然我没有在该平台上的经验,但我曾经使用过Silverlight。在这种情况下,即使它们没有标记为[Serializable](事实上,在Silverlight中它甚至不存在),您也可以序列化对象。内置的XmlSerializer只针对所有公共属性工作,而不考虑修饰。检查它是否可序列化的唯一方法是尝试序列化并尝试/捕获失败,或编写一个算法来检查每个属性(以及递归到子对象)并检查每个类型是否可以序列化。
编辑x2:查看您的ObjectStorageHelper,我建议您只需尝试序列化并捕获任何失败即可。您不一定要直接传递异常。您可以使用自己的自定义异常进行包装,或者返回结果对象,通知API使用者序列化的通过/失败及其可能失败的原因。最好假设调用者正在使用有效的对象,而不是每次都进行昂贵的检查。
编辑x3:由于您在保存方法中做了很多其他工作,我建议您像这样重写代码:
public async Task SaveAsync(T Obj)
{
    if (Obj == null)
        throw new ArgumentNullException("Obj");

    StorageFile file = null;
    StorageFolder folder = GetFolder(storageType);
    file = await folder.CreateFileAsync(FileName(Obj), CreationCollisionOption.ReplaceExisting);

    IRandomAccessStream writeStream = await file.OpenAsync(FileAccessMode.ReadWrite);
    using (Stream outStream = Task.Run(() => writeStream.AsStreamForWrite()).Result)
    {
        try
        {
            serializer.Serialize(outStream, Obj);
        }
        catch (InvalidOperationException ex)
        {
            throw new TypeNotSerializableException(typeof(T), ex);
        }

        await outStream.FlushAsync();
    }
}

这样可以捕获序列化问题,并明确向API使用者报告他们提供了一个无效/不可序列化的对象。如果I/O部分抛出异常,则更清晰地知道问题所在。实际上,您可能希望将序列化/反序列化方面分离为自己的离散方法/类,以便可以输入其他序列化器(或从堆栈跟踪中更清楚地知道问题所在,或者仅使您的方法只执行一项任务)。但是任何更多的重写/重构都要留给代码审查,而不是针对手头的问题有效。
另外,我还对您的输入对象进行了空值检查,因为如果用户传递null,则会认为保存成功,而实际上什么也没有发生,并且他们可能期望稍后加载时有一个值可用,但实际上它不存在。如果您想允许null作为有效值,则不需要检查并引发错误。

嗨,Chris, 听起来是个好建议,但是考虑到我希望这个库可以适用于几乎任何类T(这是一个类库,我希望其他人也能使用它——我并没有积极地使用它来构建应用程序),我不想仅限于我已经验证过可以在单元测试中序列化的类型子集。希望这样说得清楚。 JT - jamiet
最好假设调用者正在使用有效的对象,而不是每次都进行昂贵的检查。好建议 +1 - asawyer
@jamiet 抱歉,我看错了。 :) 既然你已经在使用 XmlSerializer,我的建议仍然适用。 - Chris Sinclair
我建议你尝试序列化并捕获任何失败。是的,这基本上就是我现在正在做的。实际上,我有一个单元测试方法(请参见http://winrtstoragehelper.codeplex.com/SourceControl/changeset/view/e695dad6ad5d#WinRtUtility%2fWinRtUtility.Test2%2fObjectStorageHelper.cs中的HandleUnSerializableObject()),用于检查问题。我想我会继续做我已经在做的事情。谢谢大家。 - jamiet
哇,太棒的反馈了。谢谢Chris。我稍后会再次审查您的评论(希望今天能做到),当我下次坐下来处理这个问题时(我和妻子有一周一次的协议,可以专注于自己的项目,而我本周已经用完了)。 - jamiet
显示剩余7条评论

2
这取决于“可序列化”的含义。使用XmlSerializer,可以将任何类序列化。如果您所说的可被序列化是指没有错误发生,那么您需要尝试并捕获异常来确切地知道。

嗨,Peter。 是的,我想我实际上的意思是“可以序列化而不会发生错误”。正如我上面所提到的,我认为try...catch是唯一确定的方法,你基本上已经证实了这一点。谢谢。JT - jamiet
不是所有的情况下,XmlSerializer都不支持循环引用和接口。 - Huusom
@Huusom 没错,找出这个问题的唯一方法就是尝试序列化该类型并捕获任何异常... - Peter Ritchie

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接