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,因为 documentdocument<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",但别无选择,因此如果它解决了您的问题,请继续使用它。