WCF Json反序列化。
我正在使用Dotnet 4.5在WCF中构建中间件Web服务,此服务器返回多态类型。
[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上创建了一个未记录的动态依赖,我必须从网上下载。
前两种反序列化方法失败了,但我找到了第三种方法。
我想知道的是为什么前两种方法失败了?理想情况下,我希望获得第一种工作方法,因为这是最精简的。
[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 :(得分: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;
我在helper方法中对formatter变量调用clear的原因是因为MediaTypeFormatterCollection的构造函数创建了默认格式化程序,我们对这些格式函数不感兴趣所以我清除它们并添加我们知道可用的1格式化程序你的解决方案。
我通常会尝试坚持DRY规则,这就是我尝试将“自定义”隔离在一个地方的原因,这样当事情发生变化时,我不需要经历所有来源并尝试记住或搜索可能已被使用过的所有实例。
以另一种方式放置东西,虽然框架确实支持你的场景,但它当然需要更改设置。如果你没有使用你所谓的多态类型,标准的开箱即用方法将会很有效。我今天早上写了一个模仿你的解决方案,我看不到一个快速的方法可以帮助你逃脱而不需要在客户端进行更改。
答案 1 :(得分:0)
在我看来,方法1和2实例化一个T类型的对象,然后通过读取流来设置它的属性。换句话说,这些方法只知道“Something”类型,因此它们只能实例化“Something”。第三种方法也使用属性DataContract和KnownType,因此它能够实例化已知类型“Something”,“SomethingA”和“SomethingB”。