2017-08-04 1 views
2

Ich bin noch ziemlich neu zu Json.Net und (De) Serialisierung. Ich habe ein Objekt, das ein paar FelderJson.Net 2 Eigenschaften auf Serialisierung kombinieren

public class Person { 
    public string Id { get; set; } 
    public string FirstName { get; set; } 
    public string LastName { get; set; } 
    public string Address1 { get; set; } 
    public string Address2 { get; set; } 
} 

var p = new Person() { 
    Id = 123, 
    FirstName = "John", 
    LastName = "Doe", 
    Address1 = "456 Main St", 
    Address2 = "Apt 2" 
} 

ich auf die folgende json

{ 
    "id": 123 
    , "fullName": "John Doe" 
    , "street": "456 Main St Apt 2" 
} 

Und deserialisieren dass

// Person.Id = 123 
// Person.Address1 = 456 Main St Apt 2 
// Person.Address2 = null 

// we are ok not trying to split the 'street' into both 'Address1' and 'Address2' 

Ich bin mir nicht sicher, dass die beste Methode zur Serialisierung benötigen hat um dies zu tun. Muß ich eine Converter verwenden, ExtensionData, Constructor, oder etwas anderes habe ich nicht gefunden, weil ich weiß nicht, wie es zu suchen;)

Jede Hilfe und Beispiele wären toll.

Antwort

0

Während @Alan Buchanan answer und @ Brian Rogers answer große Lösungen zeigen, werden diese nicht erreichen, was ich auf einfache Art und Weise benötigt.

Was am besten für mich funktionierte, war OnSerializing und OnDeserialized zu verwenden. Wenn ich etwas falsch mache, könnte das besser, oder sollte ich etwas anderes machen, lass es mich wissen.

HINWEIS: Ich verstehe, dass auf Deserialization das Feld Address2 wird leer sein, und Address1 wird eine Kombination aus dem, was Address1 und Address2 verwendet werden, aber das ist für unsere Implementierung ok.

public class Person { 
    public string Id { get; set; } 
    [JsonProperty("fullName")] 
    public string FullName { get; set; } 
    [JsonIgnore] 
    public string FirstName { get; set; } 
    [JsonIgnore] 
    public string LastName { get; set; } 
    [JsonProperty("street")] 
    public string Street { get; set; } 
    [JsonIgnore] 
    public string Address1 { get; set; } 
    [JsonIgnore] 
    public string Address2 { get; set; } 

    [OnSerializing] 
    internal void OnSerializing(StreamingContext context) { 
     Street = (Address1 + " " + Address2).Trim(); 
    } 

    [OnDeserialized] 
    internal void OnDeserialized(StreamingContext context) { 
     Address1 = Street; 
    } 
} 

// C# Object 
var p = new Person() { 
    Id = 123, 
    FirstName = "John", 
    LastName = "Doe", 
    Address1 = "456 Main St", 
    Address2 = "Apt 2" 
} 

// Json to output and consume 
{ "id": 123, "fullName": "John Doe", "street": "456 Main St Apt 2" } 
3

Um die beiden Eigenschaften kombinieren Sie eine Eigenschaft wie unten hinzufügen könnte:

public string FullName => FirstName + " " + LastName; 

Natürlich können Sie eine gewisse Logik nulls usw. wollen werden

Sie können mit Ihrer Klasse dekorieren für den Umgang mit Attribute, die bestimmen, wie die Eigenschaftsnamen serialisiert werden. Ihre Klasse könnte so etwas wie das am Ende aussehen:

using Newtonsoft.Json; 
using System.Linq; 

public class Person { 
    [JsonProperty("id")] 
    public string Id { get; set; } 

    [JsonProperty("fullName")] 
    public string FullName 
    { 
     get 
     { 
      return FirstName + " " + LastName; 
     } 
     set 
     { 
      if (value.Contains(" ")) 
      { 
       var split = value.Split(' '); 
       FirstName = split[0]; 
       LastName = string.Join(" ", split.Skip(1)); 
      } 
      else 
      { 
       FirstName = value; 
      } 
     } 
    } 

    [JsonIgnore] 
    public string FirstName { get; set; } 

    [JsonIgnore] 
    public string LastName { get; set; } 
} 

Die JsonProperty Attribute können Sie den Namen der Eigenschaft setzen, wenn es serialisiert wird, während die JsonIgnore die Serializer-Attribute sagen nicht, diese Eigenschaften in der Ausgabe enthalten.

Sie können dann erhalten Sie Ihre JSON mit:

var json = JsonConvert.SerializeObject(person); 
+0

OP sagt klar, dass er bereits JSON.net verwendet. Keine Notwendigkeit für die zusätzlichen Anweisungen in Bezug auf Newtonsoft, sie sind die gleiche Sache –

+0

Dank Alan, ich benutze Newtonsoft.Json (Ich lernte gestern die meisten Suchen kommen mit Json.Net, da es die gleiche Sache ist) =) Allerdings habe ich einige Bedenken, weil ich andere Felder habe, die ich serialisieren/deserialisieren muss, die einem ähnlichen Muster folgen und nicht möchten, dass es so eingerichtet wird. Ich werde meine Frage aktualisieren, um dies zu reflektieren. – RoLYroLLs

+0

Tut mir leid, ich neige dazu, die Frage zu vereinfachen, also ist es nicht so groß/komplex, aber dann erkenne ich, wenn gegebene Antworten, dass * ich es kompliziert gemacht habe. Ihre Antwort lohnt sich jedoch für andere Szenarien, die ich habe. – RoLYroLLs

1

@Alan Buchanan shows eine schöne einfache Art und Weise zu erreichen, was Sie in Ihrer Frage beschrieben. Wenn Sie Ihrer Klasse jedoch keine weiteren Eigenschaften hinzufügen möchten, müssen Sie stattdessen eine benutzerdefinierte JsonConverter verwenden. Hier ist ein Beispiel dafür, wie Sie es schreiben können:

class PersonConverter : JsonConverter 
{ 
    public override bool CanConvert(Type objectType) 
    { 
     return objectType == typeof(Person); 
    } 

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
    { 
     Person person = (Person)value; 
     JObject result = new JObject(); 
     result.Add("id", person.Id); 
     result.Add("fullname", (person.FirstName + " " + person.LastName).Trim()); 
     result.Add("address", (person.Address1 + " " + person.Address2).Trim()); 
     result.WriteTo(writer); 
    } 

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
    { 
     JObject obj = JObject.Load(reader); 
     Person person = new Person(); 
     person.Id = (int)obj["id"]; // assuming id will always be present in the JSON 
     string fullName = (string)obj["fullname"]; 
     if (fullName != null) 
     { 
      string[] parts = fullName.Split(new char[] { ' ' }, 2); 
      // if there's only 1 part, I'm assuming it is the first name 
      if (parts.Length > 0) 
       person.FirstName = parts[0]; 
      if (parts.Length > 1) 
       person.LastName = parts[1]; 
     } 
     person.Address1 = (string)obj["address"]; // don't bother trying to split address 
     return person; 
    } 
} 

Sie werden bemerken, die PersonConverter Klasse enthält drei Methoden, von denen alle erforderlichen durch die JsonConverter abstrakte Basisklasse implementiert werden es erbt von: CanConvert, ReadJson und WriteJson.

Die CanConvert Methode teilt Json.Net mit, dass dieser Konverter Person Objekte handhabt. Diese Methode kann von Json.Net abhängig von der Verwendung des Konverters aufgerufen werden oder nicht. Mehr dazu später.

WriteJson ist verantwortlich für das Erstellen des JSON für das Zielobjekt während der Serialisierung. Sie können hier sehen, dass ich intern ein JObject verwende, um das JSON unter Verwendung von Daten in der Klasse Person aufzubauen. Sie können Methoden auch direkt auf dem Writer aufrufen, anstatt eine JObject zu verwenden, aber ich finde es auf diese Weise etwas umständlicher.

ReadJson behandelt das Gegenteil: Rekonstruieren des Objekts aus dem JSON während der Deserialisierung. Wieder verwende ich einen JObject als Vermittler, um die Daten vom Leser zu erhalten, bevor es in die neue Person Instanz aufgeteilt wird.

Wenn Sie nur serialisiert und nicht deserialisieren benötigen, können Sie CanRead außer Kraft setzen false zurückzukehren, und dann nur ReadJson ein NotImplementedException werfen machen. In ähnlicher Weise gibt es eine CanWrite Eigenschaft, die Sie überschreiben können, wenn Sie nur die Serialisierung, aber keine Serialisierung durchführen müssen.

Es gibt zwei Möglichkeiten, eine verwenden JsonConverter: Sie können entweder eine Instanz auf die serialize/deserialize Methoden (entweder über Methodenparameter oder JsonSerializerSettings) passieren ...

string json = JsonConvert.SerializeObject(p, new PersonConverter()); 

... oder Sie verwenden können, ein [JsonConverter] Attribut den Konverter an Ihre Klasse zu binden:

[JsonConverter(typeof(PersonConverter))] 
public class Person 
{ 
    ... 
} 

Wenn Sie die erste Methode verwenden, dann Json.Net ruft CanConvert, um zu bestimmen, welche Art (en) Ihre Konverter verarbeiten kann. Wenn Sie das Attribut verwenden, wird CanConvert nie aufgerufen, da Json.Net davon ausgeht, dass Sie den korrekten Konvertierungstyp im Attribut für die Zielklasse angegeben haben.

Hier ist ein Round-Trip-Demo: https://dotnetfiddle.net/EGok23

+0

Danke! Dies scheint so zu sein, als ob ich es im Vergleich zu 'OnSerializing' machen müsste. Ich werde es ausprobieren und Sie wissen lassen, wie es funktioniert. – RoLYroLLs

+0

Brian, lass mich dich fragen, für diesen 'Konverter', der auf der' Klassen'-Ebene platziert wird, muss ich ** ALL ** der Eigenschaften hinzufügen, die ich (de) serialisieren möchte oder nur diejenigen, die ich transformieren möchte? – RoLYroLLs

+0

Ich habe meine Gedanken zu deinem Geigenbeispiel getestet und ja, ich muss alle Felder in diesen Konverter aufnehmen. Gibt es eine andere Möglichkeit, nur bestimmte Eigenschaften zu manipulieren? Ich habe Objekte mit vielen Eigenschaften, die es nicht wert sind, nur für 1 oder 2 Felder zu diesem Konverter hinzuzufügen. – RoLYroLLs

Verwandte Themen