Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Lesedauer 6 Min.

MessagePack: JSON als Binär-Image

Das Serialisierungsformat MessagePack wurde – anders als JSON – konsequent auf die Bedürfnisse von Maschinen und damit auch auf höchstmögliche Kompaktheit optimiert. Der Vorteil: Ein geringeres zu übertragendes Datenvolumen führt vor allem in IoT-nahen Anwendungen direkt zu geringeren Kosten.
© EMGenie

Unvergessen ist die Spitting-Image-Parodie der Pet Shop Boys, in der sich die beiden Protagonisten fragten, ob Kate Moss noch dünner werden könnte. Im Fall von MessagePack lautet die Antwort auf jeden Fall ja – Bild 1 zeigt, wie das System durch effizientere Nutzung von Trennzeichen und Co. dabei hilft, baren Speicherplatz einzusparen.

Dieser Beitrag will bewusst keine allgemeine Erklärung des Formats liefern. Angemerkt sei nur, dass MessagePack aus einem Typsystem und aus Formaten besteht. Das Typsystem beschreibt dabei – wie in Bild 2 gezeigt – die verschiedenen von MessagePack unterstützten Formate.

Wer die Menschenlesbarkeit opfert, gewinnt an Kompaktheit (Bild 1)

Wer die Menschenlesbarkeit opfert, gewinnt an Kompaktheit (Bild 1)

© https://msgpack.org/
MessagePack ist streng typisiert (Bild 2)

MessagePack ist streng typisiert (Bild 2)

© https://github.com/msgpack/msgpack/blob/master/spec.md

Damit eignet sich MessagePack für die Serialisierung beziehungsweise Deserialisierung unter verschiedensten Bibliotheken und Programmiersprachen – die im Folgenden vorgeführte Nutzung von C# ist explizit nur eines von mehreren möglichen Serialisierungs- beziehungsweise Deserialisierungsformaten.

Von der Qual der Wahl

Als Folge der Popularität des MessagePack-Paketformats können C#-Entwickler mittlerweile zwischen mehreren Bibliotheken wählen. In diesem Beitrag wird der Autor die bei GitHub bereitstehende Variante MessagePack-CSharp einsetzen. Sie ist am aktivsten gepflegt, erfreut sich auf NuGet der meisten Downloads und wird außerdem als herausragend schnell beschrieben (Bild 3).

Effiziente Serialisierung: nicht nur klein, sondern auch schnell (Bild 3)

Effiziente Serialisierung: nicht nur klein, sondern auch schnell (Bild 3)

© Autor

Wir erzeugen zunächst in Visual Studio 2026 ein neues Projekt auf Basis der Vorlage Konsolen-App, vergeben als Projektnamen den String MessagePack1 und entscheiden uns für die Framework-Version 8.0.

Nach der erfolgreichen Kompilation folgt der Wechsel in die NuGet-Konsole, wo wir nach dem String messagepack suchen. Achten Sie darauf, das „populärste“ Paket zu erwischen – sein Autoren-String lautet, wie in Bild 4 gezeigt, aarnot, neuecc.

Dieses Paket führt zur effizienteren Serialisierung (Bild 4)

Dieses Paket führt zur effizienteren Serialisierung (Bild 4)

© Autor

Im Interesse der Bequemlichkeit fügen wir auch noch die Erweiterungen MessagePack.Annotations und MessagePackAnalyzer zum Projektskelett hinzu. Angemerkt sei, dass der Serialisierer mit dem Paket MessagePack.UnityShims Unterstützungsbibliotheken anbietet, die auf Unity-Primitives optimiert sind. Diese wollen allerdings wir hier im Artikel außen vor lassen.

Inbetriebnahme der Serialisierungspipeline und Vergleich der Schlüsselarten

Nach der erfolgreichen Konfiguration spielen wir eine erste Serialisierung durch. Hierzu ist ein geeignetes Objekt erforderlich, das sich vorerst folgendermaßen präsentiert:

 

[MessagePackObject]
internal class NMGTesrClass {
    [Key(0)]
    public int index;
    [Key(1)]
    public String hissName;
    [Key(2)]
    public string hissSignature { get; set; }
}

 

Neben dem MessagePackObject-Attribut, das die Klasse als für Serialisierung interessantes Objekt markiert, ist auch das Key-Feld wichtig. MessagePack erkennt die individuellen Felder anhand von Indizes, die normalerweise von Hand über das Key-Attribut festzulegen sind. Wir entscheiden uns hier anfangs für numerische Indizes – die damit einhergehenden Vor- und Nachteile betrachten wir sogleich.

Vorher sei aber noch der nächste Schritt in Richtung Serialisierungspipeline durchgeführt. Der folgende Code erzeugt ein Testobjekt, um es danach unter Nutzung unserer Bibliothek in einen Bitstrom zu konvertieren. Die Rückkonversion erzeugt aus dem Bitstrom wieder ein Objekt, dessen Eigenschaften sodann in die Standardausgabe wandern:

 

static void Main(string[] args) {
    NMGTesrClass aClass = new NMGTesrClass {
        index = 1,
        hissName = "Oairi"
    };
    byte[] bytes = MessagePackSerializer.Serialize(aClass);
    NMGTesrClass mc2 = MessagePackSerializer.Deserialize<NMGTesrClass>(bytes);
    Console.WriteLine(mc2.hissName);
}

 

Bevor wir die unterschiedlichen Indizierungsarten vergleichen können, müssen wir die Ergebnisse quantifizieren. Die Ausgabe von Console.WriteLine(bytes.Length) gibt im Fall der vorliegenden Parametrierung den Wert 9 aus.

Als Nächstes verwenden wir gemäß dem nachfolgend gezeigten Schema in NMGTestClass einen String-Index – die Anpassung der an Key übergebenen Parameter ist die einzig notwendige Adaption des Programms:

 

[Key("index")]
public int index;
[Key("hissName")]
public String hissName;
[Key("hissSign")]
public string hissSignature { get; set; }

 

Nun gibt das Programm den Wert 33 an, was auf eine wesentliche Vergrößerung der zu übertragenden Datenmenge hinweist. In der Praxis ist die Situation allerdings nicht so einfach, wie sie bei reiner Betrachtung der numerischen Werte erscheint.

Tatsächlich gilt nämlich, dass ein mit numerischen Indizes serialisiertes Datenfeld direkt von den jeweiligen Werten abhängt. Kommt es im Fall einer Aktualisierung zu Änderungen in den Indizes, so schlägt die Deserialisierung fehl. Die Verwendung von String-Schlüsseln sorgt an dieser Stelle für ein robusteres Programm.

Die Einbindung des Pakets MessagePackAnalyzer war übrigens keine Leerlaufhandlung. Zur Illustration der Vorteile erweitern wir die NMGTesrClass nach folgendem Schema um einen weiteren Member, der ohne Attribute und Co. auskommen muss:

 

public string psstSignature;

 

Das Fehlen eines Attributs auf Member-Ebene führt dazu, dass der Serializer im Zweifelsfall mit undefiniertem Verhalten konfrontiert wäre. Daraus würde ein Laufzeitfehler resultieren, der dank dem Paket in VS 2026 wie in Bild 5 gezeigt dargestellt wird.

Dank der Erweiterung erkennt Visual Studio das fehlende Attribut (Bild 5)

Dank der Erweiterung erkennt Visual Studio das fehlende Attribut (Bild 5)

© Autor

Zur Lösung des Problems bietet sich entweder das Einpflegen eines weiteren Schlüssels oder das Aktivieren von IgnoreMember an – Letzteres weist den Parser dazu an, das vorliegende Objekt aus Serialisierung und Deserialisierung auszuschließen:

 

[IgnoreMember]
public string psstSignature;

Serialisierung und Deserialisierung unter Berücksichtigung des Lebenszyklus

In komplexen Einsatzszenarien kann es wünschenswert sein, ein Objekt über den Beginn einer Persisitierungsoperation zu informieren: Persistierungsrelevante Housekeeping-Tasks von normalerweise geringer Wichtigkeit könnten so beispielsweise nur dann zur Ausführung kommen, wenn sowieso eine (langsame) EA-Operation bevorsteht.

Dies lässt sich durch Realisierung des IMessagePackSerializationCallbackReceiver-Interfaces realisieren. Eine vollständige Beispielimplementierung sähe im Fall des NMGTestClass-Objekts folgendermaßen aus:

 

internal class NMGTesrClass : ImessagePackSerializationCallbackReceiver {
    public void OnBeforeSerialize(){
        Console.WriteLine("Vor Serialisierung");
    }
    public void OnAfterDeserialize() {
        Console.WriteLine("Nach Deserialisierung");
    }

 

Als Ergebnis erhalten wir das in Bild 6 gezeigte Verhalten.

Die Eventhandler feuern wie erwartet (Bild 6)

Die Eventhandler feuern wie erwartet (Bild 6)

© Autor

Bisher war die Situation des Serialisierers insofern einfach, als die zu bevölkernden Felder – in der Theorie wären auch Getter und Setter zulässig – öffentlich lesbar und schreibbar waren. Als Nächstes wollen wir uns deshalb ein Objekt ansehen, das den Entwickler mit readonly-Feldern konfrontiert:

 

[MessagePackObject]
internal class NMGROTest {
    [Key(0)]
    public readonly int X;
    [Key(1)]
    public readonly int Y;
    public NMGROTest(int x, int y) {
        this.X = x;
        this.Y = y;
    }
}

 

Die vorliegende Klasse lässt sich dank des Konstruktors gemäß dem nachfolgend gezeigten Schema bevölkern. Sofern die Auswahl der Konstruktoren beschränkt ist, findet das System durch Reflection automatisch den am besten zur vorliegenden Parametrierung passenden Kandidaten:

 

NMGROTest myROTest = new NMGROTest(2, 4);
bytes = MessagePackSerializer.Serialize(myROTest);
NMGROTest mc3 = MessagePackSerializer.Deserialize<NMGROTest>(bytes);

 

Interessant wird die Aufgabe, sobald wir – wie im Folgenden gezeigt – zwei Konstruktoren in der Klasse anlegen. Dank des Matchers würde die Rehydrierung nach wie vor unter Nutzung des mit zwei Parametern ausgestatteten Konstruktors erfolgen – logisch, da dieser ja auch zwei Felder aufweist:

 

public NMGROTest(int x, int y) {
    this.X = x;
    this.Y = y;
}
public NMGROTest(int x) {
    this.X = x;
    this.Y = 44;
}

 

Zur Fixierung der zu verwendenden Konstruktoren steht das Attribut SerializationConstructor zur Verfügung, das sich nach folgendem Schema auf eine der Methoden anwenden lässt:

 

[SerializationConstructor]
public NMGROTest(int x) {
    this.X = x;
   this.Y = 44;
}

Serialisierung und Deserialisierung unter Sicherheitsaspekten

Zu guter Letzt sei darauf hingewiesen, dass das Deserialisieren von unsicheren Strings vom Entwicklerteam als explizites Antipattern bezeichnet wird – die Verwendung von Prüfsummen sei dringend empfohlen.

Insbesondere bei der in diesem Artikel nicht näher besprochenen vollautomatisierten Deserialisierung ist es ratsam, nach folgendem Schema einen besonders paranoiden Betrieb des Parsers zu avisieren:

 

var options = MessagePackSerializerOptions.Standard
    .WithSecurity(MessagePackSecurity.UntrustedData);
T object = MessagePackSerializer.Deserialize<T>(data, options);

 

Alternativ dazu kann die folgende Zeile verwendet werden, um Standardeinstellungen zu modifizieren:

 

MessagePackSerializer.DefaultOptions = options;

Fazit

Wer an einem IoT-Projekt arbeitet, kann durch Umstellung auf MessagePack eine massive Senkung der Total Costs of Ownership erreichen. Neben den geringeren Übertragungskosten sorgt das effizientere Protokoll für schnellere Übermittlung und somit weniger Aktivzeit der energieteuren Luftschnittstelle – in Summe ein lohnender Trade-off.

Neueste Beiträge

Zwei Konferenz-Openings, eine Sprecherin – wie aus einem Terminkonflikt ein KI-Usecase wurde - Making-of eines Avatars mit HeyGen und ElevenLabs
Ein Terminkonflikt gelöst von einem digitalen Zwilling. Die Ebner Media Group hat die erste Infinite AI Conference von einem Avatar eröffnen lassen. Ein Erfahrungsbericht über den Produktionsprozess und den echten Nutzen synthetischer Präsenz.
7 Minuten
12. Aug 2026
Gebaut für lange Läufe - Kimi K3 – das Modell hinter dem Agenten, Teil 1
Kimi K3 kombiniert 2,8 Billionen Parameter, native Bildverarbeitung und ein Kontextfenster mit einer Million Token. Das sind tolle Zahlen. Seine Besonderheit liegt aber darin, dass Architektur, Training und Laufzeitumgebung auf Agenten zielen, die über viele Schritte handeln, Werkzeuge aufrufen und Ergebnisse prüfen.
6 Minuten
Wiederholend wie ein Papagei: Polly - Best of NuGet, Teil 8
Viele Probleme der Systemtechnik lassen sich durch wiederholtes Versuchen oder durch Rate Limiting lösen. Die .NET-Bibliothek Polly stellt Entwicklern hierfür verschiedenste Maßnahmen zur Resilienz-Steigerung in Form von schlüsselfertigen Bausteinen zur Verfügung.
6 Minuten
10. Aug 2026

Das könnte Dich auch interessieren

.NET-Anbindung für die Llama-KI - LLamaSharp
Open-Source-KI auf dem lokalen Gerät nutzen – mit handhabbarem Speicherbedarf und optionalem Zugriff auf externe Datenquellen.
8 Minuten
13. Jan 2025
Das Firmata-Protokoll für einfache Mess- und Steuerungsaufgaben - Best of NuGet, Teil 8
Wer kleinere Mess-, Steuerungs- und Regeltechnik-Aufgaben aus .NET heraus abwickeln möchte, kommt mit einem preiswerten und mit Firmata-Software ausgestatteten Arduino meist problemlos ans Ziel.
7 Minuten
27. Jul 2026
.NET-Color-Picker für WPF- und Avalonia-Applikationen - Best of NuGet, Teil 7
Mit PixiEditor.ColorPicker steht eine Bibliothek in den Startlöchern, die Entwicklern von Desktop-zentrierten Applikationen verschiedene hochwertige Farbauswahldialoge zur Verfügung stellt.
6 Minuten
20. Jul 2026
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige