5

我创建了以下类。一个基类 IObject 和 2 个派生类 A 和 B。

[KnownType(typeof(B))]
[KnownType(typeof(A))]
[DataContract(Name = "IObject")]
public class IObject
{
}

[DataContract(Name="A")]
public class A : IObject
{
    [DataMember]
    public string s1 { get; set; }     // Tag Name as it will be presented to the user

}

[DataContract(Name="B")]
public class B : IObject
{
    [DataMember]
    public string s2 { get; set; }         

}

我还创建了以下服务:

    [ServiceKnownType(typeof(B))]
    [ServiceKnownType(typeof(A))]
    public void GetR(IObject obj)
    {

        B other = (B)obj;
    }

我想做的是获取 A 或 B 的实例,但我不知道我会得到哪个实例,所以我希望得到一个 IObject 并稍后将其转换为 A 或 B,如我放置的示例所示。

当我发送一个包含 s2 字符串的 json 字符串时会发生什么,我得到的是 IObject 实例而不是 B 实例。代码有什么问题?

我正在使用的客户端示例:

    var url = serviceURL;
    var data = {s2: "Data"};
    var json = JSON.stringify(data);

    $.ajax({
      type: "POST",
      url: url,
      data: data,
      contentType: "application/json",
      dataType: 'json'
    });

已编辑:我已通过以下链接将示例代码上传到 gitHub: https ://github.com/AlgoEitan/27588144

那里的客户端是 ac# 客户端(我已经用 c# 和 javascript 尝试过 - 网络浏览器客户端)

4

1 回答 1

3

DataContractJsonSerializer有一种特定的格式,它存储有关多态类型的已知类型信息的提示,可以通过一些测试来发现。我按原样复制并粘贴了您的类,并创建了以下测试代码:

public static class DataContractJsonSerializerPolymorphismTest
{
    public static void Test()
    {
        var a1 = new A() { s1 = "A" };
        var b1 = new B() { s2 = "B" };

        DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(IObject));

        var jsona1 = DataContractJsonSerializerHelper.GetJson(a1, serializer);
        var jsonb1 = DataContractJsonSerializerHelper.GetJson(b1, serializer);

        Debug.WriteLine(jsona1);
        Debug.WriteLine(jsonb1);

        var newa1 = DataContractJsonSerializerHelper.GetObject<IObject>(jsona1, serializer);

        Debug.Assert(newa1.GetType() == a1.GetType()); // No assert
    }
}

这样,创建了以下 JSON:

{"__type":"A:#Tile.DataContractJsonSerializerPolymorphism","s1":"A"}
{"__type":"B:#Tile.DataContractJsonSerializerPolymorphism","s2":"B"}

Tile.DataContractJsonSerializerPolymorphism我的测试应用程序中的 CLR 命名空间的名称在哪里。因此,如您所见,已知类型信息隐藏在这个额外的 JSON 属性__type中。现在,如果您也在 DataContractJsonSerializerHelper您的客户端中使用,您将永远不会知道这一点,因为通信会正常工作。但是你使用JSON.stringify()的是没有这个逻辑的。因此,您可能必须"__type":"DataContractName:DataContractNamespace"在客户端手动添加属性。

更多关于多态类型的格式可以在这里找到。(我只是在找到隐藏参数后才跟踪这个文档__type,这给了我一个额外的谷歌搜索词。)

仅供参考,这是我在测试代码中使用的辅助类:

public static class DataContractJsonSerializerHelper
{
    private static MemoryStream GenerateStreamFromString(string value)
    {
        return new MemoryStream(Encoding.Unicode.GetBytes(value ?? ""));
    }

    public static string GetJson<T>(T obj, DataContractJsonSerializer serializer) where T : class
    {
        using (var memory = new MemoryStream())
        {
            serializer.WriteObject(memory, obj);
            memory.Seek(0, SeekOrigin.Begin);
            using (var reader = new StreamReader(memory))
            {
                return reader.ReadToEnd();
            }
        }
    }

    public static string GetJson<T>(T obj) where T : class
    {
        DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));
        return GetJson(obj, serializer);
    }

    public static T GetObject<T>(string json) where T : class
    {
        DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));
        return GetObject<T>(json, serializer);
    }

    public static T GetObject<T>(string json, DataContractJsonSerializer serializer) where T : class
    {
        T obj = null;
        using (var stream = GenerateStreamFromString(json))
        {
            obj = (T)serializer.ReadObject(stream);
        }
        return obj;
    }
}
于 2014-12-21T10:42:07.727 回答