2017-05-30 1 views
2

Ich habe eine JSON-Datei mit einem etwas seltsamen Format. Ich muss dieses Format für mein Projekt lesen, ändern und speichern. Auf das angegebene Format habe ich leider keinen Einfluss.Selektive Serialisierung von Eigenschaften einer Instanz in JSON

Das Merkwürdige an diesem Format ist, dass alle Entitäten in dieser Struktur einen eindeutigen Bezeichner haben, der (ungültigerweise) "$ id" heißt. Auf diese Instanzen kann auch anhand ihrer ID anstelle des vollständigen Satzes ihrer Eigenschaften verwiesen werden. In diesem Fall gibt es kein "$ id", stattdessen gibt es ein Feld "$ ref", das eine bereits definierte Entität enthält. Schauen Sie sich die (verkürzte) Beispiel unten:

{ 
    "$id": "1", 
    "StyleName": "Standard", 
    "Style": { 
     "$id": "2", 
     "ShapeStyle": { 
      "$id": "3", 
      "Background": { 
       "$id": "4", 
       "Color": { 
        "$id": "5", 
        "A": 255, 
        "R": 68, 
        "G": 84, 
        "B": 106 
       } 
      } 
     }, 
     "RightEndCapsStyle": { 
      "$id": "6", 
      "Background": { 
       "$id": "7", 
       "Color": { 
        "$ref": "5" 
       } 
      } 
     } 
    } 
} 

Um dies zu umgehen, habe ich eine C# Basisklasse erstellt Entity für alle JSON-Datenklassen. Diese Basisklasse verwaltet eine Registrierung von IDs, so dass sie die Instanz für eine gegebene $ ref leicht finden kann. Hier ist der Code:

public class Entity 
{ 
    public static Dictionary<string, Entity> Registry = new Dictionary<string, Entity>(); 

    private string key = string.Empty; 

    [JsonProperty("$id", Order = -2)] 
    [DefaultValue("")] 
    public string Key 
    { 
     get { return key; } 
     set 
     { 
      key = value; 
      if (!string.IsNullOrEmpty(key)) Registry.Add(key, this); 
     } 
    } 

    [JsonProperty("$ref")] 
    public string RefKey { get; set; } 

    [JsonIgnore] 
    public bool IsReference => !string.IsNullOrEmpty(RefKey); 

    [JsonIgnore] 
    public Entity RefEntity 
    { 
     get 
     { 
      Entity entity = null; 
      if (IsReference && !Registry.TryGetValue(RefKey, out entity)) 
      { 
       throw new ApplicationException("Referenced entity not found!"); 
      } 
      return entity; 
     } 
    } 
} 

Die JSON-Klasse hiearchy sieht wie folgt aus:

public class RootObject: Entity 
{ 
    public string StyleName { get; set; } 
    public StyleRoot Style { get; set; } 
} 

public class StyleRoot: Entity 
{ 
    public Style ShapeStyle { get; set; } 
    public Style RightEndCapsStyle { get; set; } 
} 

public class Style: Entity 
{ 
    public Background Background { get; set; } 
} 

public class Background: Entity 
{ 
    public Color Color { get; set; } 
} 

public class Color: Entity 
{ 
    public int A { get; set; } 
    public int R { get; set; } 
    public int G { get; set; } 
    public int B { get; set; } 
} 

So weit, so gut. Nun zu meinen Fragen:

  1. [JsonProperty("$id")] und [JsonProperty("$ref")] funktionieren nicht, da $ id und $ ref sind keine gültige JSON Feldnamen Bezeichner. Derzeit verwende ich einen regulären Ausdruck, um sie durch etwas Gültiges zu ersetzen und sie später wieder zu dem zu ersetzen, was sie einmal waren. Aber das ist träge. Ich frage mich, ob ich das NamingStrategy verwenden könnte, um dasselbe zu erreichen. Aber es ist mir nicht gelungen. Denken Sie daran, ich brauche beide Wege, Deserialisierung und Serialisierung.
  2. Die Serialisierung ist ein noch schwierigeres Problem. Wenn ich meine Struktur so serialisiere, wie ich bin, habe ich am Ende beide Felder "$id": und "$ref": in jedem Fall. Darüber hinaus werden alle anderen Felder zusammen mit dem referenzierten Element mit Standardwerten serialisiert. Der einfachste Weg war, DefaultValueHandling.IgnoreAndPopulate in JsonSerializerSettings zu setzen. Auf diese Weise werden alle Standardwerte aus der Ausgabe entfernt, was zu unerwartetem Verhalten führt. Ich habe versucht, mit ContractResolver zu gehen, aber es ist fehlgeschlagen, weil dies lokal für die aktuelle Eigenschaft ist und die umgebende Entität nicht berücksichtigt werden kann. Gibt es eine andere Möglichkeit, eine benutzerdefinierte Serialisierungsstrategie zu implementieren?

Tough Fragen, ich weiß. Und viel zu lesen und zu verstehen. Aber wenn es leicht gewesen wäre, hätte ich mir nicht die Mühe gemacht, alles hier hochzulegen. Ihre Hilfe wird dringend benötigt.

Antwort

1

Diese $id s und $ref s werden intern von Json.NET verwendet, wenn PreserveReferencesHandling mode aktiviert ist. Sie müssen sich nicht um sie kümmern, definieren Sie einfach Ihre RootObject, StyleRoot usw. Klassen normalerweise ohne diese Entity Basisklasse und setzen PreserveReferencesHandling Modus in JsonSerializerSettings. Json.NET wird mit dem $id s und $ref s unter der Haube befassen und ein Objektnetz Beibehaltung der ursprüngliche Referenzstruktur erstellen:

var obj = JsonConvert.DeserializeObject<RootObject>(json, 
    new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.All }); 

Serialisierung auch in den wahren Referenzen Handling-Modus durchgeführt werden kann:

var json = JsonConvert.SerializeObject(obj, 
    new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.Objects }); 

Demo : https://dotnetfiddle.net/TygMDZ

NB In Ihrem Eingabedatenbeispiel enthält das Attribut "$ref" einen numerischen Wert, während Json.NET schreibt und erwartet, dass "$ref" Attribute Zeichenfolgenwerte haben. Kann es sein, dass Sie versehentlich JSON-Text gepostet haben, der sich im Laufe einiger Experimente darauf geändert hat?

+0

Perfekt! Funktioniert bei mir. Sie haben Recht mit dem Wert "$ ref". Ich habe viel Text entfernt, um kompakter zu sein. Ich musste etwas manuell bearbeiten ... Was ist der Unterschied zwischen PreserveReferenceHandling.Object und PreserveReferenceHandling.All, übrigens? – freefall

Verwandte Themen