多态性 WCF 反序列化不起作用。

5

我创建了以下类。一个基类IObject和两个派生类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

客户端是C#客户端(我已经尝试过使用C#和JavaScript - Web浏览器客户端)


你是使用DataContractJsonSerializer、Json.NET还是JavaScriptSerializer? - dbc
我已经尝试了两个不同的客户端:
  1. 使用类似我上面放置的代码的Web浏览器。
  2. 我尝试了一个c#客户端,在那里我尝试了JavaScriptSerializer,之后又尝试了DataContractJsonSerializer。
这两种方法都没有任何区别——我仍然得到了一个IObject实例。
- Sandman
如果将 IObject 更改为一个 interface,会发生什么? - oatsoda
界面不起作用。 - Sandman
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参数并获得额外的Google搜索词汇后,我才跟踪到这份文档。)
附带说明一下,以下是我在测试代码中使用的帮助类:
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;
    }
}

1
我正在使用DataContractJsonSerializer(用于我的C#客户端),但它没有添加__type。 我不得不使用EmitTypeInformation.Always强制将类型添加到JSON中。 - Sandman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接