JSON verarbeiten mit Newtonsoft.Json
Sobald du in C# mit API-Antworten arbeitest oder ein zusammengesetztes Objekt in einem Global ablegen willst, kommst du an JSON nicht vorbei. Streamer.bot bringt dafür die Bibliothek Newtonsoft.Json schon mit, sie ist in jeder C#-Code-Action mit Execute standardmäßig referenziert. Du musst nichts installieren und nichts im References-Tab nachtragen, JsonConvert und JObject stehen direkt zur Verfügung. Zwei Wege gibt es: eine eigene Klasse bei festem Schema oder JObject für den lockeren Zugriff, wenn du nur ein Feld brauchst.
Doku: docs.streamer.bot · C# Methoden
Serialisieren mit JsonConvert.SerializeObject
Abschnitt betitelt „Serialisieren mit JsonConvert.SerializeObject“Serialisieren heißt: aus einem C#-Objekt einen JSON-String machen. Das brauchst du, wenn du etwas Komplexeres als eine einfache Zahl speichern oder verschicken willst. JsonConvert.SerializeObject nimmt ein beliebiges Objekt und gibt den passenden String zurück.
public class CPHInline { public bool Execute() { var spende = new { user = "azazeli", betrag = 5, nachricht = "Viel Erfolg!" };
// Objekt -> JSON-String string json = JsonConvert.SerializeObject(spende); // ergibt: {"user":"azazeli","betrag":5,"nachricht":"Viel Erfolg!"}
CPH.LogInfo(json); return true; }}Hier dient ein anonymes Objekt als Quelle. Genauso gut funktioniert eine eigene Klasse, ein Dictionary<string, object> oder eine List<T>. Newtonsoft kümmert sich um die korrekte JSON-Struktur, inklusive verschachtelter Objekte und Arrays.
Deserialisieren mit DeserializeObject
Abschnitt betitelt „Deserialisieren mit DeserializeObject“Der umgekehrte Weg: aus einem JSON-String wieder ein C#-Objekt machen. Dafür legst du eine Klasse an, deren Eigenschaften zu den JSON-Feldern passen, und gibst sie als generischen Typ-Parameter an JsonConvert.DeserializeObject<T> mit.
public class CPHInline { public class Spende { public string User { get; set; } public int Betrag { get; set; } public string Nachricht { get; set; } }
public bool Execute() { string json = "{\"user\":\"azazeli\",\"betrag\":5,\"nachricht\":\"Viel Erfolg!\"}";
// JSON-String -> typisiertes Objekt Spende s = JsonConvert.DeserializeObject<Spende>(json);
CPH.SendMessage($"💜 {s.User} hat {s.Betrag} gespendet: {s.Nachricht}"); return true; }}Die Property-Namen werden ohne Rücksicht auf Groß- und Kleinschreibung zugeordnet, User matcht also user. Eine eigene Klasse lohnt sich, sobald das Schema feststeht und du mehrere Felder typsicher weiterverarbeitest. Du bekommst Autovervollständigung auf s.User und s.Betrag und der Compiler warnt bei Tippfehlern.
Lockerer Zugriff mit JObject.Parse
Abschnitt betitelt „Lockerer Zugriff mit JObject.Parse“Wenn du nur ein einziges Feld aus einer fremden Antwort herausziehen willst, ist eine eigene Klasse mit allen Feldern Overkill. JObject.Parse parst den String in einen flexiblen Baum, auf den du per Index zugreifst. Ein typischer Fall ist eine Wetter-API, die {"main":{"temp":5}} zurückgibt und aus der du nur die Temperatur brauchst.
public class CPHInline { public bool Execute() { string json = "{\"main\":{\"temp\":5},\"name\":\"Berlin\"}";
JObject jobj = JObject.Parse(json);
// verschachtelt zugreifen: main -> temp string temp = jobj["main"]["temp"].ToString(); string ort = jobj["name"].ToString();
CPH.SendMessage($"🌡️ In {ort} sind es {temp} Grad."); return true; }}Mit jobj["main"]["temp"] hangelst du dich durch die Verschachtelung. .ToString() macht aus dem JSON-Token einen lesbaren String. Brauchst du einen typisierten Wert, geht das direkt mit jobj["main"]["temp"].Value<int>(). Ein Array iterierst du mit foreach (var eintrag in jobj["liste"]).
Klasse oder JObject
Abschnitt betitelt „Klasse oder JObject“Beide Wege parsen denselben JSON-String, die Wahl hängt am Anwendungsfall.
| Situation | Empfehlung |
|---|---|
| Festes Schema, viele Felder, mehrfach genutzt | eigene Klasse mit DeserializeObject<T> |
| Nur ein, zwei Felder aus einer fremden Antwort | JObject.Parse und per Index zugreifen |
| Schema ändert sich oft oder ist nicht dokumentiert | JObject, weil du keine Klasse pflegen musst |
| Daten werden typsicher weiterverarbeitet | eigene Klasse, du bekommst Autovervollständigung |
Faustregel: feste, wiederkehrende Struktur ruft nach einer Klasse. Mal eben ein Feld aus einer API-Antwort rausziehen ruft nach JObject.
Komplexe Objekte in einem Global speichern
Abschnitt betitelt „Komplexe Objekte in einem Global speichern“CPH.SetGlobalVar nimmt einen object-Wert, aber bei verschachtelten Objekten oder Listen fährst du sicherer, wenn du selbst zu einem String serialisierst und ihn ablegst. Beim Lesen deserialisierst du zurück. So überlebt eine komplette Liste den Restart, ohne dass du dich auf das interne Serialisieren verlassen musst.
public class CPHInline { public class Quote { public string Text { get; set; } public string Author { get; set; } }
public bool Execute() { // bestehende Liste laden, null-sicher string raw = CPH.GetGlobalVar<string>("quotes", true); var quotes = raw == null ? new List<Quote>() : JsonConvert.DeserializeObject<List<Quote>>(raw);
// neues Zitat anhängen quotes.Add(new Quote { Text = "GG WP", Author = "azazeli" });
// wieder als JSON-String persistieren CPH.SetGlobalVar("quotes", JsonConvert.SerializeObject(quotes), true);
CPH.SendMessage($"📜 Zitate gespeichert: {quotes.Count}"); return true; }}Beim ersten Aufruf liefert GetGlobalVar<string> ein null, dann startest du mit einer leeren Liste. Danach steht im Global ein JSON-Array, das du jedes Mal lädst, ergänzt und zurückschreibst. Mehr zum dauerhaften Ablegen größerer Datenmengen steht unter Daten-Persistenz.
Häufige Fallen
Abschnitt betitelt „Häufige Fallen“- Generics-Tippfehler bei
DeserializeObject<T>: Der Typ-Parameter muss zur JSON-Struktur passen. Ein JSON-Array deserialisierst du nachList<Quote>, ein einzelnes Objekt nachQuote. Verwechselst du das, wirft Newtonsoft eineJsonSerializationException. - Fehlende Felder: Fehlt ein Feld im JSON, ist die entsprechende Property im Objekt
null(bei Referenztypen) oder der Default0(beiint). Vor dem Weiterverarbeiten prüfen, sonst gibt es eineNullReferenceException. BeiJObjectliefert ein nicht existenter Schlüssel wiejobj["fehlt"]ebenfallsnull, und.ToString()darauf kracht. - Falscher Typ beim Auslesen:
jobj["main"]["temp"].Value<int>()auf einem Feld, das in Wahrheit ein String wie"5°C"ist, wirft einen Konvertierungsfehler. Im Zweifel.ToString()nehmen und selbst parsen. - Verschachtelte Arrays: Bei
{"items":[{"id":1}]}istjobj["items"]ein Array, kein Objekt. Du iterierst mitforeach (var i in jobj["items"])und greifst dann aufi["id"]zu. Direktjobj["items"]["id"]schlägt fehl, weil ein Array keinen Feldnamen kennt. - Manuelles String-Bauen statt SerializeObject: JSON per
$"{{\"key\":\"{wert}\"}}"zusammenstückeln bricht, sobaldwertein Anführungszeichen oder einen Umlaut enthält. ImmerJsonConvert.SerializeObjectnutzen, das escaped korrekt.