【问题标题】:Can anyone recommend a way to check if a class can be serialized as XML?谁能推荐一种方法来检查一个类是否可以序列化为 XML?
【发布时间】:2012-09-05 20:06:27
【问题描述】:

我有一个通用类,它接受一个 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,感谢您的回复。不,我不只是想检查它是否具有该属性。我对其他一些回复的 cmets 希望能解释为什么不这样做。

标签: c# .net windows-runtime


【解决方案1】:

AFAIK,即使您检查各种属性(SerializableDataContract)或检查Type.IsSerializable(我认为这只是检查Serializable 属性存在的一种方便方法)它不会保证实现实际上可序列化的。 (编辑:如前所述,并在问题中提供的示例代码中看到,XmlSerializer 不依赖于 Serializable 属性装饰。因此检查这些标志是没有意义的。)

根据我的经验,最好的办法是使用单元测试来验证应用程序中使用的各种类型,并使用 try/catch 来查看它是否通过/失败。在运行时,使用 try/catch(而不是每次都预先检查)并记录/处理异常。

如果您通过单元测试获得了有效兼容类型的列表,您可以根据您之前通过测试确定的编译时列表对 T 进行预检查,并假设任何其他类型只是没有好处。可能想要注意已知有效类型的子类,即使它们继承自有效的可序列化类型,它们的实现也可能不是。

编辑:由于这是针对 Windows Phone 8 的,虽然我没有使用该平台的经验,但我使用过 Silverlight。在这种情况下,您可以序列化对象,即使它们没有标记为[Serializable](事实上,它甚至在 Silverlight 中都不存在)。内置的XmlSerializer 仅适用于所有公共属性,无论装饰如何。查看它是否可序列化的唯一方法是尝试序列化并尝试/捕获失败,或者编写一个算法来检查每个属性(并通过子对象递归)并检查每种类型是否可以序列化。

EDITx2:查看您的ObjectStorageHelper,我建议您简单地尝试序列化并捕获任何故障。您不一定要直接冒泡异常。您可以使用自己的自定义异常进行包装,或者返回一个结果对象,通知 API 使用者序列化的通过/失败以及它可能失败的原因。最好假设调用者正在使用一个有效的对象,而不是每次都进行昂贵的检查。

EDITx3:由于您在保存方法中做了很多其他工作,我建议您像这样重写您的代码:

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 部分中抛出异常,那么问题出在哪里就更清楚了。实际上,您可能希望将序列化/反序列化方面分离到它们自己的离散方法/类中,以便您可以输入其他序列化程序(或者从堆栈跟踪中更清楚问题所在,或者只是让您的方法做一个一件事和一件事)但是无论如何,任何更多的重写/重构实际上都留给代码审查,并且对于手头的问题没有多大意义。

仅供参考,我还为您的输入对象设置了一个空值检查,因为如果用户传递空值,他们会认为保存成功,而事实上,没有发生任何事情,他们可能期望一个值当它不存在时可用于稍后加载。如果你想允许空值作为有效值,那么不要为 check 抛出错误而烦恼。

【讨论】:

  • 嗨,克里斯,听起来不错,但是考虑到我希望它几乎适用于任何 T 类(这是一个我希望其他人使用的类库 - 我不积极使用它来构建应用程序)我真的不想仅限于我已经验证可以在我的单元测试中序列化的子集类型。希望这是有道理的。 JT
  • Better to assume the caller is using a valid object rather than doing an expensive check each time. 好建议 +1
  • XmlSerializer 不需要 SerializableAttribute 序列化和类的实例。
  • @jamiet 完全可以。请参阅我最新的“EDITx2”。本质上,将责任放在 API 的使用者身上,以确保他们的类型是可序列化的。如果他们给你垃圾,处理它,或者冒泡异常或者向他们报告你想要的。只需记录可序列化的需求(也许您可以提及您希望它们与XmlSerializer 兼容并将它们指向适当的 MSDN 文档)
  • P.S.克里斯,这并不重要,但这适用于 Windows 8,而不是 Windows Phone 8。
【解决方案2】:

这取决于“可以序列化”是什么意思。任何类都可以用XmlSerializer 序列化。如果您所说的可以序列化是指没有错误发生,那么您必须尝试捕获异常才能确定。

【讨论】:

  • 嗨 Peter 是的,我想我的真正意思是“可以序列化而不会发生错误”。正如我在上面提到的那样,我认为 try...catch 将是唯一可以确定的方法,而您几乎已经确认了这一点。谢谢。 JT
  • 不是全部,XmlSerializer 不支持循环引用和接口。
  • @Huusom 对,唯一的方法是尝试序列化类型并捕获任何异常......
猜你喜欢
  • 2011-01-06
  • 2017-06-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多