2014-10-28 37 views
1

WCF Json反序列化。WCF Json反序列化保存多态集合类型

我正在WCF中使用Dotnet 4.5构建一个中间件webservice,该服务器返回一个多态类型。

[DataContract] 
[KnownType(typeof(SomethingA))] 
[KnownType(typeof(SomethingB))] 
public class Something 
{ 
    [DataMember] 
    public int Item1 { get; set; } 

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

[DataContract] 
public class SomethingA : Something 
{ } 

[DataContract] 
public class SomethingB : Something 
{ } 


/// <summary> 
/// Contract for a service for testing various web operations. 
/// </summary> 
[ServiceContract] 
[ServiceKnownType(typeof(SomethingA))] 
[ServiceKnownType(typeof(SomethingB))] 
public interface ITesting 
{ 
    /// <summary> 
    /// Test passing in and returning an object using POST and json. 
    /// </summary> 
    [OperationContract] 
    [WebInvoke(
     RequestFormat = WebMessageFormat.Json, 
     ResponseFormat = WebMessageFormat.Json, 
     BodyStyle = WebMessageBodyStyle.Bare, 
     UriTemplate = "use-polymorphic-somethings", 
     Method = "POST")] 
    List<Something> UsePolymorphicSomethings(); 
} 

/// <summary> 
/// Implementation of the ITesting service contract. 
/// </summary> 
public class Testing : ITesting 
{ 
    public List<Something> UsePolymorphicSomethings() 
    { 
     List<Something> retVal = new List<Something>(); 
     retVal.Add(new SomethingA { Item1 = 1, Item2 = "1" }); 
     retVal.Add(new SomethingB { Item1 = 1, Item2 = "1" }); 
     return retVal; 
    } 
} 

在客户端,我试图反序列化这种方式来保存集合中的不同类型。 MSDN文档对我来说似乎非常薄弱。我遇到的第一个问题是添加一个对System.Web.Http的引用,在第三方开源组件Newtonsoft.Json上创建了一个未公开的动态依赖关系,我必须从Web上下载它。

前两种反序列化方法失败,但我发现了第三种方法。

我想知道的是为什么前两种方法失败?理想情况下,我希望得到第一种工作方式,因为这是最简化的。

[TestMethod] 
public void UsePolymorphicSomethings_Test1() 
{ 
    using (HttpClient http = new HttpClient()) 
    { 
     http.BaseAddress = new Uri("http://localhost:8733/"); 

     HttpResponseMessage response = http.PostAsJsonAsync(
     "Design_Time_Addresses/InSite8WebServiceLib2/Testing/use-polymorphic-somethings", 
     new StringContent(string.Empty)).Result; 

     List<Something> ret = response.Content.ReadAsAsync<List<Something>>().Result; 

     // FAILS. 
     Assert.AreEqual(typeof(SomethingA), somethings[0].GetType()); 
     Assert.AreEqual(typeof(SomethingB), somethings[1].GetType()); 
    } 
} 

[TestMethod] 
public void UsePolymorphicSomethings_Test2() 
{ 
    using (HttpClient http = new HttpClient()) 
    { 
     http.BaseAddress = new Uri("http://localhost:8733/"); 

     HttpResponseMessage response = http.PostAsJsonAsync(
     "Design_Time_Addresses/InSite8WebServiceLib2/Testing/use-polymorphic-somethings", 
     new StringContent(string.Empty)).Result; 

     string ret1 = response.Content.ReadAsStringAsync().Result; 
     Newtonsoft.Json.JsonSerializerSettings s = new Newtonsoft.Json.JsonSerializerSettings(); 
     s.TypeNameHandling = Newtonsoft.Json.TypeNameHandling.All; 
     List<Something> r = Newtonsoft.Json.JsonConvert.DeserializeObject<List<Something>>(ret1, s); 

     // FAILS. 
     Assert.AreEqual(typeof(SomethingA), somethings[0].GetType()); 
     Assert.AreEqual(typeof(SomethingB), somethings[1].GetType()); 
    } 
} 

[TestMethod] 
public void UsePolymorphicSomethings_Test3() 
{ 
    using (HttpClient http = new HttpClient()) 
    { 
     http.BaseAddress = new Uri("http://localhost:8733/"); 

     HttpResponseMessage response = http.PostAsJsonAsync(
     "Design_Time_Addresses/InSite8WebServiceLib2/Testing/use-polymorphic-somethings", 
     new StringContent(string.Empty)).Result; 

     Stream stream = response.Content.ReadAsStreamAsync().Result; 
     DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(List<Something>)); 
     List<Something> somethings = (List<Something>)serializer.ReadObject(stream); 

     // SUCCEEDS. 
     Assert.AreEqual(typeof(SomethingA), somethings[0].GetType()); 
     Assert.AreEqual(typeof(SomethingB), somethings[1].GetType()); 
    } 
} 
+0

嗨,我相信最后一种方法工作的原因,其他两个没有。是因为当您使用WebMessageFormat.Json时,使用服务器端的序列化程序是DataContractJsonSerializer。所以使用相同的串行器服务器和客户端都是有道理的,最终会取得令人满意的结果。例如,如果你想使用NewstonSoft,你必须侧身,创建一个消息格式化类,一个Web http行为,一个行为扩展元素和一个web内容类型映射器,然后你需要把所有这些都绑定起来,可能是网页或应用程序配置文件。 – 2014-11-04 05:56:07

+0

我明白了。在这种情况下,UsePolymorphicSomethings_Test1必须使用不同的具有JSON能力的反序列化器(因为它确实执行反序列化,只是不正确),这引发了几个问题。哪个JSON序列化程序默认使用ReadAsAsync?为什么它默认不使用DataContractJsonSerializer?并且可以将ReadAsAsync配置为默认使用DataContractJsonSerializer,因为它的默认配置是无用的? – Neutrino 2014-11-07 13:33:57

+0

如果我不必使用Newtonsoft,我真的不想使用Newtonsoft,我尝试使用Newtonsoft类反序列化的唯一原因是因为我的集成测试程序集中的System.Net.Http创建了一个动态(运行时)对它的依赖这让我猜想它可能以某种方式与这个问题有关。 – Neutrino 2014-11-07 13:34:26

回答

2

根据我的理解,您对您编写的代码的流内容感到“担心”。看到你的代码在你的最后一个方法中工作,并且我希望我的理由能够工作,而其他人不会让你满意。我们仍然可以使用一个简单的帮助程序对您的方法进行流式处理。

public T Deserialize<T>(Stream stream) where T : class 
    { 
     var serializer = new DataContractJsonSerializer(typeof(T)); 
     return (T)serializer.ReadObject(stream); 
    } 

然后你可以简单地调用这个方法,像这样

List<Something> somethings = Deserialize<List<Something>>(stream); 

要在某种意义上使事情也许更容易,你可以写辅助方法为扩展方法, 像这样

public static class Helpers 
{ 
    public static T Deserialize<T>(this Stream stream) where T : class 
    { 
     var serializer = new DataContractJsonSerializer(typeof(T)); 
     return (T)serializer.ReadObject(stream); 
    } 
} 

然后,您可以调用这个方法这样

var result = stream.Deserialize<List<Something>>(); 

要一路走下去,你可以对HttpResponseMessage创建扩展方法

public static class Helpers 
{ 
    public static T Deserialize<T>(this HttpResponseMessage response) where T : class 
    { 
     var stream = response.Content.ReadAsStreamAsync().Result; 
     var serializer = new DataContractJsonSerializer(typeof(T)); 
     return (T)serializer.ReadObject(stream); 
    } 
} 

你可以调用这个方法这样

var result = response.Deserialize<List<Something>>(); 

至少你的代码将现在又是一个班轮,如果你将来改变你的序列化程序,你只需要在一个地方改变你的客户代码。您可能需要检查代码,因为我目前没有打开Visual Studio为您测试它。但对我来说看起来不错。

我在这里添加了一个新的帮助程序示例,因此有更多的选项/修复程序供您选择。

public static class Helpers 
{ 
    public static Task<T> ReadAsAsyncCustom<T>(this HttpContent content) 
    { 
     var formatters = new MediaTypeFormatterCollection(); 
     formatters.Clear(); 
     formatters.Add(new JsonMediaTypeFormatter { UseDataContractJsonSerializer = true }); 
     return content.ReadAsAsync<T>(formatters); 
    } 
} 

和如下

List<Something> ret = response.Content.ReadAsAsyncCustom<List<Something>>().Result; 

我之所以呼吁在辅助方法格式化变量明确这一个可以被使用,是因为MediaTypeFormatterCollection的构造函数创建默认格式化,我们是不是对这些内容感兴趣,所以我清除它们并添加一个我们知道适用于您的解决方案的格式化程序。

我一般尽量坚持到干燥的原则,这是我努力保持“定制”分离只是一个单一的地方,所以,当事情发生变化,我并不需要经过所有的源代码并试着记住的原因或搜索可能已被使用的所有实例。

把东西用另一种方式为好,而框架并支持你的情况下,它当然需要,如果你将一个设置更改。如果你不使用你所谓的多态类型,那么标准的开箱即用方法将会很好。我今天早上写了一个模拟你的解决方案,我看不到一个快速的方法,可以帮助你在客户端没有改变的情况下离开。

+0

对不起,我可能不够清楚。我试图了解什么反序列化机制ReadAsAsync使用它导致它反序列化JSON不正确,不只隐藏另一种方法的解决方法代码。 – Neutrino 2014-11-07 13:44:50

+0

嗨中微子,谢谢你的清理。事实上,Jose实际上触及了这一点。客户端的问题与他所说的完全相同,并且按照他所述发送参数确实解决了问题,但是,无论哪种方式,您都必须“改变”代码的工作方式。所以除非你只在应用程序中有一个调用,否则我仍然建议你使用带有扩展方法的辅助类,或者至少沿着这些方向。至少在我的例子中,“定制”是在一个地方,应该在未来发生变化,变化很简单 – 2014-11-08 02:51:02

+0

因此,除了上述示例工作,我为你准备,现在我要更新它,以包括一种方法,你可以选择最适合你的方式。 – 2014-11-08 02:51:57

0

在我看来,方法1和2实例化一个T类型的对象,然后通过读取流来设置它的属性。换句话说,这些方法只知道类型“Something”,因此它们只能实例化“Something”。第三种方法也使用属性DataContract和KnownType,因此它能够实例化已知类型“Something”,“SomethingA”和“SomethingB”。

enter image description here

+0

这正在发生。我试着去了解_为什么会发生这种情况。在UsePolymorphicSomethings_Test1的ReadAsAsync方法必须使用某种形式的反序列化的内部,该JSON传递明确包含有利于多态的反序列化所需的类型信息(因为UsePolymorphicSomethings_Test3正常工作),所以反序列化机制在默认情况下使用,为什么没有按什么ReadAsAsync”问题仍然是t正确地反序列化。 – Neutrino 2014-11-07 13:41:17

+0

ReadAsAsync未正确序列化,因为它没有使用DataContract和KnownType属性。默认情况下,ReadAsAsync使用UseDataContractJsonSerializer属性设置为[JsonMediaTypeFormatter](http://msdn.microsoft.com/en-us/library/system.net.http.formatting.jsonmediatypeformatter%28v=vs.118%29.aspx)假。如果要使用这些属性进行序列化,则需要使用DataContractJsonSerializer或ReadAsAsync传递new [] {new JsonMediaTypeFormatter {UseDataContractJsonSerializer = true}}。我编辑了我最初的回应,为此设置了一个例子。 – 2014-11-07 23:19:18