Der beste Weg, um mit dieser Situation umzugehen, ist die Verwendung eines benutzerdefinierten JsonConverter
.
Bevor wir zum Konverter gelangen, müssen wir eine Klasse definieren, in die die Daten deserialisiert werden sollen. Categories
Definieren Sie die Eigenschaft, die zwischen einem einzelnen Element und einem Array variieren kann, als List<string>
und markieren Sie sie mit einem [JsonConverter]
Attribut, damit JSON.Net den benutzerdefinierten Konverter für diese Eigenschaft verwenden kann. Ich würde auch empfehlen, [JsonProperty]
Attribute zu verwenden, damit den Elementeigenschaften unabhängig von der Definition in JSON aussagekräftige Namen zugewiesen werden können.
class Item
{
[JsonProperty("email")]
public string Email { get; set; }
[JsonProperty("timestamp")]
public int Timestamp { get; set; }
[JsonProperty("event")]
public string Event { get; set; }
[JsonProperty("category")]
[JsonConverter(typeof(SingleOrArrayConverter<string>))]
public List<string> Categories { get; set; }
}
Hier ist, wie ich den Konverter implementieren würde. Beachten Sie, dass ich den Konverter generisch gemacht habe, damit er bei Bedarf mit Zeichenfolgen oder anderen Objekttypen verwendet werden kann.
class SingleOrArrayConverter<T> : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(List<T>));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JToken token = JToken.Load(reader);
if (token.Type == JTokenType.Array)
{
return token.ToObject<List<T>>();
}
return new List<T> { token.ToObject<T>() };
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Hier ist ein kurzes Programm, das den Konverter in Aktion mit Ihren Beispieldaten demonstriert:
class Program
{
static void Main(string[] args)
{
string json = @"
[
{
""email"": ""[email protected]"",
""timestamp"": 1337966815,
""category"": [
""newuser"",
""transactional""
],
""event"": ""open""
},
{
""email"": ""[email protected]"",
""timestamp"": 1337966815,
""category"": ""olduser"",
""event"": ""open""
}
]";
List<Item> list = JsonConvert.DeserializeObject<List<Item>>(json);
foreach (Item obj in list)
{
Console.WriteLine("email: " + obj.Email);
Console.WriteLine("timestamp: " + obj.Timestamp);
Console.WriteLine("event: " + obj.Event);
Console.WriteLine("categories: " + string.Join(", ", obj.Categories));
Console.WriteLine();
}
}
}
Und schließlich ist hier die Ausgabe der oben genannten:
email: [email protected]
timestamp: 1337966815
event: open
categories: newuser, transactional
email: [email protected]
timestamp: 1337966815
event: open
categories: olduser
Geige: https://dotnetfiddle.net/lERrmu
BEARBEITEN
Wenn Sie in die andere Richtung gehen müssen, dh serialisieren, während Sie das gleiche Format beibehalten, können Sie die WriteJson()
unten gezeigte Methode des Konverters implementieren . (Stellen Sie sicher, dass Sie die CanWrite
Überschreibung entfernen oder in "Zurückgeben" ändern, da sie true
sonst WriteJson()
niemals aufgerufen wird.)
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
List<T> list = (List<T>)value;
if (list.Count == 1)
{
value = list[0];
}
serializer.Serialize(writer, value);
}
Geige: https://dotnetfiddle.net/XG3eRy
DeserializeObject
Aufruf hinzufügen, wenn Sie das[JsonConverter]
Attribut für die List-Eigenschaft in Ihrer Klasse verwenden, wie in der obigen Antwort gezeigt. Wenn Sie das Attribut nicht verwenden, müssen Sie den Konverter an übergebenDeserializeObject
.List<T>
im Konverter aufT[]
und ändern Sie.Count
auf.Length
. dotnetfiddle.net/vnCNgZIch habe ewig daran gearbeitet und danke Brian für seine Antwort. Alles, was ich hinzufüge, ist die Antwort von vb.net!:
dann in deiner Klasse:
Hoffe das spart dir etwas Zeit
quelle
Als kleine Variation der großartigen Antwort von Brian Rogers sind hier zwei optimierte Versionen von
SingleOrArrayConverter<T>
.Erstens ist hier eine Version, die für alle
List<T>
für jeden Typ funktioniertT
, der selbst keine Sammlung ist:Es kann wie folgt verwendet werden:
Anmerkungen:
Der Konverter vermeidet die Notwendigkeit, den gesamten JSON-Wert als
JToken
Hierarchie in den Speicher vorzuladen.Der Konverter gilt nicht für Listen, deren Elemente auch als Sammlungen serialisiert sind, z
List<string []>
Das
canWrite
an den Konstruktor übergebene boolesche Argument steuert, ob Einzelelementlisten als JSON-Werte oder als JSON-Arrays neu serialisiert werden sollen.Der Konverter
ReadJson()
verwendet dasexistingValue
vorab zugewiesene if, um das Auffüllen von Mitgliedern der Nur-Get-Liste zu unterstützen.Zweitens ist hier eine Version, die mit anderen generischen Sammlungen wie z
ObservableCollection<T>
:Wenn Ihr Modell dann beispielsweise eine verwendet
ObservableCollection<T>
für einigeT
können Sie es wie folgt anwenden:Anmerkungen:
SingleOrArrayListConverter
muss derTCollection
Typ lesen / schreiben und einen parameterlosen Konstruktor haben.Demo-Geige mit grundlegenden Unit-Tests hier .
quelle
Ich hatte ein sehr ähnliches Problem. Meine Json-Anfrage war mir völlig unbekannt. Ich wusste es nur.
Darin befinden sich eine objectId und einige anonyme Schlüsselwertpaare UND Arrays.
Ich habe es für ein EAV-Modell verwendet, das ich gemacht habe:
Meine JSON-Anfrage:
Meine Klasse habe ich definiert:
und jetzt, da ich unbekannte Attribute mit ihrem Wert und den darin enthaltenen Arrays deserialisieren möchte, sieht mein Konverter so aus:
Jedes Mal, wenn ich ein AnonymObject erhalte, kann ich das Wörterbuch durchlaufen und jedes Mal, wenn mein Flag "ValueDummyForEAV" vorhanden ist, wechsle ich zur Liste, lese die erste Zeile und teile die Werte auf. Danach lösche ich den ersten Eintrag aus der Liste und fahre mit der Iteration aus dem Wörterbuch fort.
Vielleicht hat jemand das gleiche Problem und kann es benutzen :)
Grüße Andre
quelle
Sie können a
JSONConverterAttribute
wie hier verwenden: http://james.newtonking.com/projects/json/help/Vorausgesetzt, Sie haben eine Klasse, die aussieht
Sie würden die Kategorie Eigenschaft wie hier gezeigt dekorieren:
quelle
Um dies zu handhaben, müssen Sie einen benutzerdefinierten JsonConverter verwenden. Aber das haben Sie wahrscheinlich schon gedacht. Sie suchen nur einen Konverter, den Sie sofort verwenden können. Und das bietet mehr als nur eine Lösung für die beschriebene Situation. Ich gebe ein Beispiel mit der gestellten Frage.
So benutze ich meinen Konverter:
Platzieren Sie ein JsonConverter-Attribut über der Eigenschaft.
JsonConverter(typeof(SafeCollectionConverter))
Und das ist mein Konverter:
Und dieser Konverter verwendet die folgende Klasse:
Was macht es genau? Wenn Sie das Konverterattribut platzieren, wird der Konverter für diese Eigenschaft verwendet. Sie können es für ein normales Objekt verwenden, wenn Sie ein JSON-Array mit 1 oder keinem Ergebnis erwarten. Oder Sie verwenden es an einem Ort, an
IEnumerable
dem Sie ein JSON-Objekt oder ein JSON-Array erwarten. (Wissen, dass einarray
-object[]
- ein istIEnumerable
) Ein Nachteil ist, dass dieser Konverter nur über einer Eigenschaft platziert werden kann, weil er glaubt, alles konvertieren zu können. Und sei gewarnt . Astring
ist auch einIEnumerable
.Und es bietet mehr als eine Antwort auf die Frage: Wenn Sie nach etwas anhand der ID suchen, wissen Sie, dass Sie ein Array mit einem oder keinem Ergebnis zurückerhalten. Die
ToObjectCollectionSafe<TResult>()
Methode kann das für Sie erledigen.Dies kann für Single Result vs Array mit JSON.net verwendet werden und behandelt sowohl ein einzelnes Element als auch ein Array für dieselbe Eigenschaft und kann ein Array in ein einzelnes Objekt konvertieren.
Ich habe dies für REST-Anforderungen auf einem Server mit einem Filter gemacht, der ein Ergebnis in einem Array zurückgegeben hat, aber das Ergebnis als einzelnes Objekt in meinem Code zurückbekommen wollte. Und auch für eine OData-Ergebnisantwort mit erweitertem Ergebnis mit einem Element in einem Array.
Viel Spass damit.
quelle
Ich habe eine andere Lösung gefunden, die die Kategorie mithilfe von Objekten als Zeichenfolge oder Array behandeln kann. Auf diese Weise muss ich mich nicht mit dem JSON-Serializer anlegen.
Bitte schauen Sie mal rein, wenn Sie Zeit haben und sagen Sie mir, was Sie denken. https://github.com/MarcelloCarreira/sendgrid-csharp-eventwebhook
Es basiert auf der Lösung bei https://sendgrid.com/blog/tracking-email-using-azure-sendgrid-event-webhook-part-1/ aber ich habe auch die Datumskonvertierung vom Zeitstempel hinzugefügt und die Variablen entsprechend aktualisiert aktuelles SendGrid-Modell (und dafür gesorgt, dass Kategorien funktionieren).
Ich habe auch einen Handler mit Basisauthentifizierung als Option erstellt. Siehe die ashx-Dateien und die Beispiele.
Danke dir!
quelle