Azure Service-Fabric 和 DocumentDB 消息序列化问题
Azure Service-Fabric and DocumentDB message serialization issue
所以,- 在我的 DocumentDB 中,我可能有以下文档:
{
"id": 1,
"type": "A",
"content": {
"x": 1,
"y": 2
}
}
此模型可能支持:
public class acontent
{
public int x { get; set; }
public int y { get; set; }
}
public class document
{
public int id { get; set; }
public string type { get; set; }
public object content { get; set; }
}
public class documenta : document
{
public new acontent content { get; set; }
}
这里的想法是文档是一个复杂的对象,其内容可能因类型而异。
现在,- 在我的 ServiceFabric 应用程序中,我有一个从 DocumentDB 读取的无状态微服务,当从 ServiceProxy.
这里的问题是 DocumentDB SDK 中的 DocumentQuery 在查询数据库时使用 Json.NET 序列化程序,而 servicefabric 使用 DataContractSerializer 序列化服务消息.
因此,当 document class 的 content 部分从 DocumentDB 反序列化时,它变为:
Newtonsoft.Json.Linq.JObject
但是当它通过 returned 服务消息序列化回来时,您会得到异常:
Type 'Newtonsoft.Json.Linq.JToken' is a recursive collection data
contract which is not supported. Consider modifying the definition of
collection 'Newtonsoft.Json.Linq.JToken' to remove references to
itself.
为说明此问题,请尝试以下代码:
using System;
using System.IO;
using System.Text;
using System.Runtime.Serialization.Json;
using Newtonsoft.Json;
namespace jsoinissue
{
public class acontent
{
public int x { get; set; }
public int y { get; set; }
}
public class document
{
public int id { get; set; }
public string type { get; set; }
public object content { get; set; }
}
public class documenta : document
{
public new acontent content { get; set; }
}
public class Program
{
private const string JSON_A = "{\"id\":1,\"type\":\"A\",\"content\":{\"x\":1,\"y\":2}}";
private static string SerializeObject<T> (T obj)
{
try
{
DataContractJsonSerializer js = new DataContractJsonSerializer(typeof(T));
using (var ms = new MemoryStream())
{
js.WriteObject(ms, obj);
ms.Position = 0;
using (var sr = new StreamReader(ms))
return sr.ReadToEnd();
}
}
catch (Exception e)
{
return String.Format("EXCEPTION: {0}",e.Message);
}
}
public static void Main()
{
var A = JsonConvert.DeserializeObject<document>(JSON_A);
var a = SerializeObject<document>(A);//HERE BE TROUBLE
Console.WriteLine(a);
Console.ReadKey();
}
}
}
我怎样才能最好地解决这个问题?
您的基本问题是 DataContractJsonSerializer
不支持未类型化的 free-form JSON 数据。正如 Working with untyped JSON in a WCF service the System.Json
中所解释的那样,为此目的将命名空间添加到 Silverlight,但它似乎从未进入完整的 .Net class 库。
相反,在您的无状态微服务中可以执行 嵌套序列化 ,其中 free-form JSON 在使用数据进行序列化时表示为转义字符串文字合约序列化器。因此你的 classes 看起来像这样:
[DataContract]
[JsonObject]
public abstract class documentbase
{
[DataMember]
[JsonProperty]
public int id { get; set; }
[DataMember]
[JsonProperty]
public string type { get; set; }
[IgnoreDataMember]
[JsonProperty("content")]
public abstract JToken JsonContent { get; set; }
[JsonIgnore]
[DataMember(Name = "content")]
string DataContractContent
{
get
{
if (JsonContent == null)
return null;
return JsonContent.ToString(Newtonsoft.Json.Formatting.None);
}
set
{
if (string.IsNullOrEmpty(value))
JsonContent = null;
else
JsonContent = JToken.Parse(value);
}
}
}
[DataContract]
[JsonObject]
public class document : documentbase
{
JToken content;
public override JToken JsonContent { get { return content; } set { content = value; } }
}
[DataContract]
[JsonObject]
public class document<T> : documentbase where T : class
{
[IgnoreDataMember]
[JsonIgnore]
public T Content { get; set; }
public override JToken JsonContent
{
get
{
if (Content == null)
return null;
return JToken.FromObject(Content);
}
set
{
if (value == null || value.Type == JTokenType.Null)
Content = null;
else
Content = value.ToObject<T>();
}
}
}
然后 SerializeObject<document>(A)
生成的 JSON 将如下所示:
{
"content":"{\"x\":1,\"y\":2}",
"id":1,
"type":"A"
}
然后,在接收系统上,您可以使用数据协定序列化程序反序列化为 document
,然后使用 LINQ to JSON 查询反序列化的 JToken JsonContent
。或者,如果接收系统知道需要 document<acontent>
,它可以反序列化数据协定 JSON,因为 document
和 document<T>
具有相同的数据协定。
您是否考虑过从 DataContractSerializer 改为支持更好的序列化程序? Here's 如何插入不同的序列化程序。
class InitializationCallbackAdapter
{
public Task OnInitialize()
{
this.StateManager.TryAddStateSerializer(new MyStateSerializer());
return Task.FromResult(true);
}
public IReliableStateManager StateManager { get; set; }
}
class MyStatefulService : StatefulService
{
public MyStatefulService(StatefulServiceContext context)
: this(context, new InitializationCallbackAdapter())
{
}
public MyStatefulService(StatefulServiceContext context, InitializationCallbackAdapter adapter)
: base(context, new ReliableStateManager(context, new ReliableStateManagerConfiguration(onInitializeStateSerializersEvent: adapter.OnInitialize)))
{
adapter.StateManager = this.StateManager;
}
}
这可能是 newtonsoft 或其他。此外,我相信该方法当前标记为 "Deprecated",但别无选择,因此如果它解决了您的问题,请继续使用它。
所以,- 在我的 DocumentDB 中,我可能有以下文档:
{
"id": 1,
"type": "A",
"content": {
"x": 1,
"y": 2
}
}
此模型可能支持:
public class acontent
{
public int x { get; set; }
public int y { get; set; }
}
public class document
{
public int id { get; set; }
public string type { get; set; }
public object content { get; set; }
}
public class documenta : document
{
public new acontent content { get; set; }
}
这里的想法是文档是一个复杂的对象,其内容可能因类型而异。
现在,- 在我的 ServiceFabric 应用程序中,我有一个从 DocumentDB 读取的无状态微服务,当从 ServiceProxy.
这里的问题是 DocumentDB SDK 中的 DocumentQuery 在查询数据库时使用 Json.NET 序列化程序,而 servicefabric 使用 DataContractSerializer 序列化服务消息.
因此,当 document class 的 content 部分从 DocumentDB 反序列化时,它变为:
Newtonsoft.Json.Linq.JObject
但是当它通过 returned 服务消息序列化回来时,您会得到异常:
Type 'Newtonsoft.Json.Linq.JToken' is a recursive collection data contract which is not supported. Consider modifying the definition of collection 'Newtonsoft.Json.Linq.JToken' to remove references to itself.
为说明此问题,请尝试以下代码:
using System;
using System.IO;
using System.Text;
using System.Runtime.Serialization.Json;
using Newtonsoft.Json;
namespace jsoinissue
{
public class acontent
{
public int x { get; set; }
public int y { get; set; }
}
public class document
{
public int id { get; set; }
public string type { get; set; }
public object content { get; set; }
}
public class documenta : document
{
public new acontent content { get; set; }
}
public class Program
{
private const string JSON_A = "{\"id\":1,\"type\":\"A\",\"content\":{\"x\":1,\"y\":2}}";
private static string SerializeObject<T> (T obj)
{
try
{
DataContractJsonSerializer js = new DataContractJsonSerializer(typeof(T));
using (var ms = new MemoryStream())
{
js.WriteObject(ms, obj);
ms.Position = 0;
using (var sr = new StreamReader(ms))
return sr.ReadToEnd();
}
}
catch (Exception e)
{
return String.Format("EXCEPTION: {0}",e.Message);
}
}
public static void Main()
{
var A = JsonConvert.DeserializeObject<document>(JSON_A);
var a = SerializeObject<document>(A);//HERE BE TROUBLE
Console.WriteLine(a);
Console.ReadKey();
}
}
}
我怎样才能最好地解决这个问题?
您的基本问题是 DataContractJsonSerializer
不支持未类型化的 free-form JSON 数据。正如 Working with untyped JSON in a WCF service the System.Json
中所解释的那样,为此目的将命名空间添加到 Silverlight,但它似乎从未进入完整的 .Net class 库。
相反,在您的无状态微服务中可以执行 嵌套序列化 ,其中 free-form JSON 在使用数据进行序列化时表示为转义字符串文字合约序列化器。因此你的 classes 看起来像这样:
[DataContract]
[JsonObject]
public abstract class documentbase
{
[DataMember]
[JsonProperty]
public int id { get; set; }
[DataMember]
[JsonProperty]
public string type { get; set; }
[IgnoreDataMember]
[JsonProperty("content")]
public abstract JToken JsonContent { get; set; }
[JsonIgnore]
[DataMember(Name = "content")]
string DataContractContent
{
get
{
if (JsonContent == null)
return null;
return JsonContent.ToString(Newtonsoft.Json.Formatting.None);
}
set
{
if (string.IsNullOrEmpty(value))
JsonContent = null;
else
JsonContent = JToken.Parse(value);
}
}
}
[DataContract]
[JsonObject]
public class document : documentbase
{
JToken content;
public override JToken JsonContent { get { return content; } set { content = value; } }
}
[DataContract]
[JsonObject]
public class document<T> : documentbase where T : class
{
[IgnoreDataMember]
[JsonIgnore]
public T Content { get; set; }
public override JToken JsonContent
{
get
{
if (Content == null)
return null;
return JToken.FromObject(Content);
}
set
{
if (value == null || value.Type == JTokenType.Null)
Content = null;
else
Content = value.ToObject<T>();
}
}
}
然后 SerializeObject<document>(A)
生成的 JSON 将如下所示:
{
"content":"{\"x\":1,\"y\":2}",
"id":1,
"type":"A"
}
然后,在接收系统上,您可以使用数据协定序列化程序反序列化为 document
,然后使用 LINQ to JSON 查询反序列化的 JToken JsonContent
。或者,如果接收系统知道需要 document<acontent>
,它可以反序列化数据协定 JSON,因为 document
和 document<T>
具有相同的数据协定。
您是否考虑过从 DataContractSerializer 改为支持更好的序列化程序? Here's 如何插入不同的序列化程序。
class InitializationCallbackAdapter
{
public Task OnInitialize()
{
this.StateManager.TryAddStateSerializer(new MyStateSerializer());
return Task.FromResult(true);
}
public IReliableStateManager StateManager { get; set; }
}
class MyStatefulService : StatefulService
{
public MyStatefulService(StatefulServiceContext context)
: this(context, new InitializationCallbackAdapter())
{
}
public MyStatefulService(StatefulServiceContext context, InitializationCallbackAdapter adapter)
: base(context, new ReliableStateManager(context, new ReliableStateManagerConfiguration(onInitializeStateSerializersEvent: adapter.OnInitialize)))
{
adapter.StateManager = this.StateManager;
}
}
这可能是 newtonsoft 或其他。此外,我相信该方法当前标记为 "Deprecated",但别无选择,因此如果它解决了您的问题,请继续使用它。