0

Also, - in meinem DocumentDB kann ich folgendes Dokument habe:Azure-Service-Fabric und DocumentDB Nachricht Serialisierung Ausgabe

{ 
    "id": 1, 
    "type": "A", 
    "content": { 
    "x": 1, 
    "y": 2 
    } 
} 

, die von diesem Modell unterstützt werden kann:

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; } 
    } 

Die Idee hier Ist dieses Dokument ein komplexes Objekt, kann der Inhalt je nach Typ variieren.

Nun - in meiner ServiceFabric Anwendung habe ich eine stateless Micro, die von DocumentDB liest und sollte ein Dokument Typ Objekt zurück, wenn aus dem ServiceProxy genannt.

Das Problem dabei ist, dass die DocumentQuery aus dem DocumentDB SDK, Json.NET Serializer verwendet, wenn die Datenbank abfragen, während servicefabric DataContractSerializer verwendet für die Service-Nachrichten Serialisierung. So

wenn die Inhalt Teil Dokument Klasse aus dem DocumentDB deserialisiert wird es wird:

Newtonsoft.Json.Linq.JObject 

Aber wenn es zurück durch die zurücke Service-Nachricht ist serialisiert erhalten Sie die Ausnahme:

Typ 'Newtonsoft.Json.Linq.JToken' ist ein rekursiver Sammlungsdaten Vertrag, der nicht unterstützt wird. Erwägen Sie, die Definition der Kollektion 'Newtonsoft.Json.Linq.JToken' zu ändern, um Verweise auf selbst zu entfernen.

dieses Problem zu veranschaulichen den Folowing Code versuchen:

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(); 
     } 
    } 
} 

Wie könnte ich dieses Problem am besten lösen?

+0

'DataContractJsonSerializer' unterstützt nicht wirklich das Deserialisieren und Serialisieren von JSON-Daten in freier Form, so wie' DataContractSerializer' keine freien XML-Daten unterstützt. Ich würde vorschlagen, dass Sie Ihr Datenmodell vollständig statisch ändern. Ist das möglich? – dbc

+0

dbc, - Nun, ich habe gehofft, eine Lösung zu entwickeln, die in der Lage ist, solche komplexen Typen zu handhaben. Der Microservice sollte nur nach ID und Typ fragen, wo die weitere Serialisierung vom Service-Aufrufer downstream durchgeführt werden soll. – ifxdev

+0

Ist der Inhalt immer ein einfaches JSON-Objekt oder könnte er mehrere verschachtelte Ebenen von Objekten oder Arrays enthalten? I.e. Wäre ein 'Dictionary ' ausreichend? – dbc

Antwort

0

Haben Sie sich stattdessen mit einer besseren Unterstützung von DataContractSerializer zu einem Serializer geändert? Here's wie Sie einen anderen Serializer anschließen würden.

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; 
    } 
} 

Dies könnte Newton oder was auch immer sein. Auch ich glaube, dass die Methode derzeit als "Veraltet" markiert ist, aber es gibt keine Alternative, also, wenn es Ihr Problem löst, gehen Sie voran und verwenden Sie es.

+0

Vielen Dank für Ihre Antwort, ich schätze es. Im Moment kämpfe ich mit der Entwicklung der 'MyStateSerializer' Klasse, um Newtonsoft für die Serialisierung zu verwenden. Irgendein Rat? – ifxdev

1

Ihr grundlegendes Problem ist, dass DataContractJsonSerializer keine nicht typisierten JSON-Daten in freier Form unterstützt. Wie in Working with untyped JSON in a WCF service erläutert wurde der Namespace System.Json zu diesem Zweck zu Silverlight hinzugefügt, aber es scheint, dass es nie in die vollständige .Net-Klassenbibliothek gelangte.

Stattdessen kann in Ihrem statuslosen Microservice eine geschachtelte Serialisierung, wo die Freiform-JSON als Escape-String-Literal bei der Serialisierung mit dem Datenvertrag Serializer dargestellt wird. So Ihre Klassen wie folgt aussehen würde:

[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>(); 
     } 
    } 
} 

Dann wird die von SerializeObject<document>(A) erzeugt JSON wird wie folgt aussehen:

{ 
    "content":"{\"x\":1,\"y\":2}", 
    "id":1, 
    "type":"A" 
} 

Dann auf dem Empfangssystem, können Sie zu einem document deserialisieren den Datenvertrag mit Serializer, dann fragen Sie die deserialized JToken JsonContent mit LINQ to JSON. Wenn das empfangende System eine document<acontent>-Nachricht erwartet, kann es alternativ den Datenvertrag JSON als solchen deserialisieren, da document und document<T> identische Datenverträge haben.