C# Azure服务结构和DocumentDB消息序列化问题
因此,-在我的文档数据库中,我可能有以下文档:C# Azure服务结构和DocumentDB消息序列化问题,c#,json,serialization,azure-service-fabric,azure-cosmosdb,C#,Json,Serialization,Azure Service Fabric,Azure Cosmosdb,因此,-在我的文档数据库中,我可能有以下文档: { "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 {
{
"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类的内容部分从DocumentDB反序列化时,它将变成:
Newtonsoft.Json.Linq.JObject
但是,当通过返回的服务消息将其序列化回时,会出现异常:
类型“Newtonsoft.Json.Linq.JToken”是一个递归数据集合
不受支持的合同。考虑修改定义
集合“Newtonsoft.Json.Linq.JToken”以删除对的引用
本身
要说明此问题,请尝试以下代码:
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();
}
}
}
使用系统;
使用System.IO;
使用系统文本;
使用System.Runtime.Serialization.Json;
使用Newtonsoft.Json;
名称空间问题
{
公共类内容
{
公共整数x{get;set;}
公共整数y{get;set;}
}
公共类文档
{
公共int id{get;set;}
公共字符串类型{get;set;}
公共对象内容{get;set;}
}
公共类文档:文档
{
公共新内容{get;set;}
}
公共课程
{
私有常量字符串JSON_A=“{\'id\':1,\'type\':\'A\',\'content\':{\'x\':1,\'y\':2}}”;
私有静态字符串序列化对象(T obj)
{
尝试
{
DataContractJsonSerializer js=新的DataContractJsonSerializer(typeof(T));
使用(var ms=new MemoryStream())
{
js.WriteObject(ms,obj);
ms.Position=0;
使用(var sr=新的StreamReader(ms))
返回sr.ReadToEnd();
}
}
捕获(例外e)
{
返回String.Format(“异常:{0}”,e.Message);
}
}
公共静态void Main()
{
var A=JsonConvert.DeserializeObject(JSON_A);
var a=SerializeObject(a);//这里有麻烦了
控制台写入线(a);
Console.ReadKey();
}
}
}
如何才能最好地解决此问题?您的基本问题是
DataContractJsonSerializer
不支持非类型化、自由格式的JSON数据。正如前面所解释的,名称空间是为此目的添加到Silverlight的,但它似乎从未进入完整的.Net类库
相反,在无状态微服务中,当使用数据协定序列化程序进行序列化时,可以执行嵌套序列化,其中自由格式JSON表示为转义字符串文本。因此,您的类将如下所示:
[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>();
}
}
}
然后,在接收系统上,您可以使用数据协定序列化程序反序列化为
文档
,然后使用查询反序列化的JToken JsonContent
。或者,如果接收系统知道需要一个文档
,它可以反序列化数据契约JSON,因为文档
和文档
具有相同的数据契约。您是否考虑过将DataContractSerializer改为支持更好的序列化器?如何插入不同的序列化程序
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或其他什么。此外,我认为该方法目前标记为“不推荐”,但是没有其他选择,因此如果它解决了您的问题,请继续使用它
DataContractJsonSerializer
实际上不支持反序列化和序列化自由格式的JSON数据,就像DataContractSerializer
不支持自由格式的XML数据一样。我建议将您的数据模型更改为完全静态类型。这可能吗?dbc,-嗯,我有点希望构建一个能够处理如此复杂类型的解决方案。微服务应该只查询id和类型,服务的调用者应该在下游执行进一步的序列化。内容总是一个简单的JSON对象,还是可以有多个嵌套级别的对象或数组?例如,字典
是否足够?它也可以是多个嵌套级别的对象。我看不出字典在这里是如何工作的,请解释一下?如果你有多个层次的话就不会了。它只适用于你问题中所示的单一级别,因此我要求澄清。谢谢你的回答,我对此表示感谢。现在,我正在努力研究如何设计mystatesializer
类来使用Newtonsoft进行序列化。有什么建议吗?
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;
}
}