【问题标题】:How can I deserialize an object containing a DataTable with enum values using DataContractSerializer?如何使用 DataContractSerializer 反序列化包含具有枚举值的 DataTable 的对象?
【发布时间】:2013-06-14 17:45:44
【问题描述】:

假设我在一个名为 ClassLibrary1 的模块中有这个简单的类:

[DataContract]
public class Class1
{
    [DataMember]
    private DataTable _data;

    public void SetData(string key, object value)
    {
        _data = new DataTable("SomeName");
        _data.Columns.Add("Key", typeof(String));   
        _data.Columns.Add("Value", value.GetType());
        _data.Rows.Add(key, value);
    }
}

在一个名为 ClassLibrary2 的单独模块中,我有以下类:

[DataContract]
public class Configuration
{
    [DataMember]
    private Class1 _obj;

    public Configuration()
    {
        _obj = new Class1();
        _obj.SetData("Key", MyEnum.Value2);
    }
}

此外,模块ClassLibrary2 定义了一个名为MyEnum 的枚举,它没有标记为公共,即它在模块内部(如您所见,在上面的代码中使用)。

现在,在我的主模块中,我不想依赖ClassLibrary2,而是需要在运行时动态加载它。也就是说,我使用Assembly.LoadFrom,然后找到我需要使用的类型(在我的简单示例中,我只搜索名为“Configuration”的类型),并使用Activator.CreateInstance 创建一个实例。然后我像这样序列化实例:

var ser = new DataContractSerializer(config.GetType());
var outstream = new FileStream("c:\\test.xml", FileMode.Create);
ser.WriteObject(outstream, config);
outstream.Close();

到目前为止,一切正常。但是当我尝试像这样反序列化它时:

var instream = new FileStream("c:\\test.xml", FileMode.Open);
var conf = ser.ReadObject(instream);

我收到一个ArgumentException,其中包含消息“列需要有效的数据类型”。我发现如果我也不会发生这种情况

  • 改为在主模块中定义 MyEnum
  • 用简单的String key; Object value 替换DataTable
  • 使用其他东西代替枚举 MyEnum(例如字符串)

但在我的实际应用中,这些选项都不是非常理想的。第一个选项将在不应相互依赖的模块之间创建强耦合。第二个选项是可能的,但工作量很大(我已经在课堂上很多地方使用了 DataTable,直到最近才发现我必须进行序列化),第三个选项也有点乱。

还有其他方法可以让它工作吗?我还尝试通过 DataContractSerializer 构造函数和在 Configuration 类声明前面附加 [KnownType(typeof(MyEnum))] 将 MyEnum 添加到序列化程序的已知类型列表中,但这没有帮助。

【问题讨论】:

  • 你能发布你的 Enum 声明吗?
  • 当然:枚举 MyEnum { Value1, Value2, Value3 }
  • 您是否尝试将您的枚举标记为[DataContract],并将每个成员标记为[EnumMemberAttribute]?
  • 试过了,但得到了完全相同的错误。
  • 刚刚在我的身上试了一下,没有报错。

标签: c# enums datatable datacontractserializer


【解决方案1】:

底层代码只是找不到自动写在 test.xml 文件中的 ClassLibrary2 程序集全名。它需要一些帮助。

如果您挂钩 AppDomain.AssemblyResolve Event 之前的反序列化,您可以解决此问题,如下所示:

AppDomain.CurrentDomain.AssemblyResolve += CurrentDomainAssemblyResolve;
...
var instream = new FileStream("test.xml", FileMode.Open);
var conf = ser.ReadObject(instream);
...

private static Assembly CurrentDomainAssemblyResolve(object sender, ResolveEventArgs args)
{
    if (args.Name == "ClassLibrary2, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null") // adapt to your needs of course
        return Assembly.LoadFrom(@"mypath\ClassLibrary2.dll");

    return null; // don't know for this one
}

【讨论】:

  • 好吧,这行得通,但我不明白为什么。在我的程序开始时,我已经有了Assembly.LoadFrom(@"c:\ClassLibrary2.dll");。那么为什么我需要告诉系统再次加载这个文件呢?
  • 它不能按原样工作,因为底层序列化代码调用了 Type.GetType("ClassLibrary2.MyEnum, ClassLibrary2, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null") (一个程序集限定类型名称),但只有在 ClassLibrary2 可以自动解析时才有效(例如,如果它位于 .EXE 路径中)。如果在路径中复制 ClassLibrary2.dll,它也可以工作。更多信息:blogs.msdn.com/b/suzcook/archive/2003/05/30/…
猜你喜欢
  • 2019-12-07
  • 2014-05-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-28
  • 1970-01-01
  • 1970-01-01
  • 2020-08-08
相关资源
最近更新 更多