DataContract序列化 - 我可以序列化Type属性吗?

时间:2012-03-11 21:21:06

标签: c# json wcf

我正在努力提供通用的JSON序列化/反序列化服务作为WCF服务。

为了做到这一点,我有以下DataContract

[DataContract]
public class SerializationRequest
{
     [DataMember]
     public Object Data {get;set;}     //Object to be serialized

     [DataMember]
     public string Type {get;set;}       //Type of the Data object

}

我遇到的问题是我收到以下错误:

  

InnerException消息是'Type'System.RuntimeType',数据协定名称为'RuntimeType:http://schemas.datacontract.org/2004/07/System'不是预期的。考虑使用DataContractResolver或将任何静态未知的类型添加到已知类型列表中 - 例如,通过使用KnownTypeAttribute属性或将它们添加到传递给DataContractSerializer的已知类型列表中。有关详细信息,请参阅InnerException。

这告诉我,我需要让DataContractJSONSerializer知道该类型,或者Type无法序列化。

我已经尝试注册对象的类型并将其添加到Serializers“Known Types”,但这似乎不起作用,这让我相信我的问题在于我的DataContract上有一个Type类型的参数。

我怎样才能做到这一点?我需要能够调用以下代码(“ish”)。

        DataContractJsonSerializer serializer = new DataContractJsonSerializer(request.Type);
        MemoryStream ms = new MemoryStream();
        serializer.WriteObject(ms, request.Data);
        string json = Encoding.Default.GetString(ms.ToArray());
        ms.Dispose();

        return new JSONSerializationResponse() {Data = json};

修改

我已将type参数更改为要序列化的类型的完全限定名称,但是当我在远程WCF服务中调用以下代码时,我仍然遇到同样的问题:

        Type type = Type.GetType(request.Type);
        DataContractJsonSerializer serializer = new DataContractJsonSerializer(type, new Type[]{Type.GetType(request.Type)});
        MemoryStream ms = new MemoryStream();
        serializer.WriteObject(ms, request.Data);
        string json = Encoding.Default.GetString(ms.ToArray());
        ms.Dispose();

        return new JSONSerializationResponse() {Data = json};

我已经尝试将创建的类型添加到DataContractJSONSerializer的构造函数中的KnownTypes,但这不起作用......任何想法?

以下是一个需要能够序列化的简单类的示例

[DataContract]
[KnownType(typeof(Person))]
public class Person
{
    [DataMember]
    public string Age {get;set;}

    [DataMember]
    public strign Name {get;set;}
}

所以现在我应该能够通过指定类型名称将它传递给我的请求对象,然后返回该对象的JSON结果。

3 个答案:

答案 0 :(得分:2)

我不确定为什么你需要JSON中的.NET类型,但你可以将Type序列化为字符串,然后显然从字符串创建一个Type。

您可以使用Type.FullName和Assembly.GetType(“My.Type”)来获取字符串(序列化)并从名称(反序列化)创建类型。

答案 1 :(得分:1)

你这么做太难了。

鉴于以下内容:

[DataContract]
public class Person : BaseObject
{
    [DataMember]
    public string Age { get; set; }

    [DataMember]
    public string Name { get; set; }
}

[DataContract]
[KnownType(typeof(Person))]
public class BaseObject
{
}

static string Serialize<T>(T item)
{
    DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));

    string result;
    using (var ms = new MemoryStream())
    {
        serializer.WriteObject(ms, item);
        result = Encoding.Default.GetString(ms.ToArray());
    };
    return result;
}

您可以序列化一个人:

        var person = new Person { Name = "Me", Age = Int16.MaxValue.ToString() };
        Console.WriteLine(Serialize<Person>(person));
        // Produces: {"Age":"32767","Name":"Me"}

在这种情况下,序列化程序的工作基础是可以将其反序列化为单个已知类型。无论谁将这些数据拉出来都知道会发生什么,所以这种类型不会被推入。

或者您可以序列化BaseObject:

    var person = new Person { Name = "Me", Age = Int16.MaxValue.ToString() };
    Console.WriteLine(Serialize<BaseObject>(person));
    // Produces: {"__type":"Person:#ConsoleApplication6","Age":"32767","Name":"Me"}

此处序列化程序看到您提供的是派生类型,但该类型是“已知”(即两端都知道可能的类型)。因此它使用类型提示进行序列化。您不需要再做任何事情了。该框架为您处理所有这些。

在任何地方放置KnownTypes都会让对手感到痛苦,因此DataContractJsonSerializer构造函数的另一个重载就派上用场了,所以你可以在运行时而不是通过属性来指定它们。

希望有所帮助。

答案 2 :(得分:0)

您无法使用Type本身,因为在运行时它将是RuntimeType的实例 - 这是内部类,因此无法添加到ServiceKnownType属性(或更精确 - 无法通过WCF使用的DataContractSerializer序列化。

您应该考虑将类型的程序集限定名称序列化为简单字符串。这将允许您在客户端重新创建类型:

[DataContract]
public class SerializationRequest
{
     // ...

     [DataMember]
     public string TypeName {get;set;}
}

// ...

var type = Type.GetType(response.TypeName);
var serializer = new DataContractJsonSerializer(type);