【问题标题】:How can I serialize a tuple containing a basic type and an array of a basic type?如何序列化包含基本类型和基本类型数组的元组?
【发布时间】:2013-05-07 22:29:21
【问题描述】:

在 C# 4.0 中,我尝试使用 DataContractSerializer 序列化和反序列化 Tuple<Guid, int[]>。我已经成功序列化和反序列化类型Guid、类型int[] 和类型Tuple<Guid, int>。如果我尝试序列化类型Tuple<Guid, int[]>,一切都会编译,但会出现以下运行时异常:

Type 'System.Int32[]' with data contract name 
'ArrayOfint:http://schemas.microsoft.com/2003/10/Serialization/Arrays'
is not expected. Consider using a DataContractResolver or add any types
not known statically to the list of known types - for example, by using
the KnownTypeAttribute attribute or by adding them to the list of known 
types passed to DataContractSerializer.

我的序列化和反序列化例程很简单:

public static string Serialize<T>(this T obj)
{
    var serializer = new DataContractSerializer(obj.GetType());
    using (var writer = new StringWriter())
    using (var stm = new XmlTextWriter(writer))
    {
        serializer.WriteObject(stm, obj);
        return writer.ToString();
    }
}

public static T Deserialize<T>(this string serialized)
{
    var serializer = new DataContractSerializer(typeof(T));
    using (var reader = new StringReader(serialized))
    using (var stm = new XmlTextReader(reader))
    {
        return (T)serializer.ReadObject(stm);
    }
}

为什么我会收到此异常,我可以做些什么来解决或绕过它?在我看来,包含可序列化类型的 Tuple 应该不会有序列化问题。

【问题讨论】:

  • 您是否尝试过错误消息提示的任何操作?
  • 我查看了 DataContractResolver,但我不确定如何以通常适用于 Tuples 中基本类型或集合类型的任意组合的方式使用它。到目前为止,我发现的所有示例都只使用了一个已知的预先存在的类。我需要它来为Tuple&lt;Guid, int[]&gt;Tuple&lt;int[], int[]&gt;Tuple&lt;int[], int&gt; 等以及具有 2 或 3 个项目/集合的元组工作,最好不用一堆案例来测试每个组合。

标签: c# c#-4.0 serialization tuples datacontractserializer


【解决方案1】:

这样的事情怎么样?

class Program
{
    public static class DataContractSerializerFactory<T>
    {
        private static IEnumerable<Type> GetTypeArguments(Type t, IEnumerable<Type> values)
        {
            if (t.IsGenericType)
                foreach (var arg in t.GetGenericArguments())
                    values = values.Union(GetTypeArguments(arg, values));
            else
                values = values.Union(new[] { t });
            return values;
        }

        public static DataContractSerializer Create()
        {
            return new DataContractSerializer(typeof(T), GetTypeArguments(typeof(T), new[] { typeof(T) }));
        }
    }

    static void Main(string[] args)
    {
        var x = Tuple.Create(Guid.NewGuid(), new[] { 1, 2, 3, 4, 5, 6 });

        var serializer = DataContractSerializerFactory<Tuple<Guid, int[]>>.Create();

        var sb = new StringBuilder();
        using (var writer = XmlWriter.Create(sb))
        {
            serializer.WriteObject(writer, x);
            writer.Flush();
            Console.WriteLine(sb.ToString());
        }
    }
}

编辑:应该适用于任何嵌套泛型。仅考虑基本类型和叶类型参数。如果您希望中间容器也成为 KnownTypes 的一部分,这应该很容易。

【讨论】:

  • 嗯,首先我得到了一个例外,即GetGenericTypeDefinition() 只支持泛型类型,当我的代码的另一部分调用我的序列化方法时,我的序列化方法被修改为创建一个具有上述工厂的序列化器和一个非泛型类型,但在添加对 typeof(T).IsGenericType 的检查以解决该问题后,我仍然遇到与 OP 中相同的错误。
  • 有这个代码?对于列出的示例,我似乎没有错误。仅当 t.IsGenericType 为真时才应调用 GetGenericTypeDefinition。还可以尝试新编辑中的代码。我会很高兴看到一个炸弹样本,以便我可以修复它。
  • 我指的是你之前的代码,因为我还没有看到你的更新。我现在正在测试这个版本。
  • 仍然看到同样的异常。我将使用调用代码以及更新的序列化代码来更新 OP。
  • 我复制了您的代码 - 在 VS 2012 下运行(并针对 .NET 4.5)没有异常,请参见此处的 pastebin (pastebin.com/5Mp3FZf9)
【解决方案2】:

错误信息告诉你需要做什么;问题在于,本身是嵌入在序列化类型中的成员的数组不会立即被序列化程序识别。您只需为其提供序列化该特定类型所需的元数据。

通常你会通过在DataContact 上添加一个属性来做到这一点,如下所示:

[DataContract]
[KnownType(typeof(int[]))]

但是由于您要序列化 ​​Tuple 而不是用户定义的类,因此您需要将其传递给 the constructor:

var knownTypes = new List<Type> { typeof(int[]) };
var serializer = new DataContractSerializer(typeof(Tuple<Guid, int[]>), knownTypes);

【讨论】:

  • 这种方法有效,而且效果很好!即使使用我的自定义类型;我只是将 typeof(mytype), typeof(mytype[]) 添加到 knownTypes。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-07-12
  • 2012-07-19
  • 2011-05-23
  • 1970-01-01
  • 1970-01-01
  • 2012-10-08
相关资源
最近更新 更多