[C#] JSON serialisieren/deserialisieren

JSON-Daten liegen in einem eigenen Format vor. Um dieses Datenformat in C# abzubilden benötigt man zuerst eine oder mehrere Klassen. Der Einfachheit halber kann man sich auf der Seite http://json2csharp.com/ aus dem JSON-Datenformat automatisiert C#-Klassen bauen lassen.

Als Beispiel dient folgende JSON-Datei:

{
"SerialNumber": "05857628",
"DateTime": "1415783932",
"Meters": [{
"InputNumber": "1",
"Name": "HZ Wirkleistung BZ 90612021",
"Type": "Electricity",
"Value": 2092224.000,
"Unit": "Wirkleistung"
},
{
"InputNumber": "2",
"Name": "HZ Blindleistung BZ 90612021",
"Type": "Electricity",
"Value": 2093763.500,
"Unit": "Blindleistung"
},
]
}

Die zugehörigen C#-Klassen sehen wie folgt aus:

using System.Collections.Generic;
using System.Runtime.Serialization;

namespace JSONTest
{
    [DataContract]
    public class Meter
    {
        [DataMember]
        public string InputNumber { get; set; }
        [DataMember]
        public string Name { get; set; }
        [DataMember]
        public string Type { get; set; }
        [DataMember]
        public double? Value { get; set; }
        [DataMember]
        public string Unit { get; set; }
    }

    [DataContract]
    public class Meter_List
    {
        [DataMember]
        public string SerialNumber { get; set; }
        [DataMember]
        public string DateTime { get; set; }
        [DataMember]
        public List<Meter-> Meters { get; set; }
    }
}

Im Code greift man nun folgendermaßen auf die Daten zu:

// im Projektbaum Verweis hinzufügen: System.Runtime.Serialization
using System.Net;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;

private void btnGetJSON_Click(object sender, EventArgs e)
{
	// JSON-File von URL holen
	string contents = new System.Net.WebClient().DownloadString("www.datenquelle.de");

	// JSON deserialisieren
	DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(Meter_List));
	MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(contents));
	Meter_List obj = (Meter_List)ser.ReadObject(stream);

	// Daten ausgeben
	Console.WriteLine(obj.SerialNumber);
	Console.WriteLine(obj.Meters.Count);
	Console.WriteLine(obj.Meters[0].Name);
}

Quelle: Serializing and Deserializing JSON in C#

[C#] String in Enum wandeln

public enum MyType
{
    Undef = 0,
    Type1 = 10,
    Type2 = 20,
    Type3 = 64
}

// Variante 1 mit Parse
string sType = &quot;Type1&quot;;
MyType t = (MyType)Enum.Parse(typeof(MyType), sType);

// Variante 2 mit TryParse
MyType a;
string sType = &quot;Type1&quot;;

// Inhalt von string &quot;sType&quot; bestimmen und in MyType wandeln
// Ergebnis in &quot;a&quot; ausgeben
if (Enum.TryParse<MyType->(sType, out a))
{
    ...
}

Generische Listenklasse mit Indexer und Enumerator

using System;
using System.Collections;
using System.Collections.Generic;
/// <summary>
/// generic list class with indexer and enumerator
/// freeware 2011 by admin of codezentrale.6x.to
/// </summary>
/// <typeparam name="T">datatype you want to manage</typeparam>
public class GenericList<T> : IEnumerable, IDisposable
{
    /// <summary>
    /// internal class, with interface for IEnumerable and IEnumerator
    /// </summary>
    private class GenericListEnumerator : IEnumerator
    {
        private int pos = -1;
        private GenericList<T> _t;

        public GenericListEnumerator(GenericList<T> t)
        {
            this._t = t;
        }

        public bool MoveNext()
        {
            if (pos < _t.Count - 1)
            {
                pos++;
                return true;
            }
            else
            {
                return false;
            }
        }

        public void Reset()
        {
            pos = -1;
        }

        public object Current
        {
            get
            {
                try
                {
                    return _t[pos];
                }
                catch (IndexOutOfRangeException)
                {
                    throw new InvalidOperationException();
                }
            }
        }
    }

    private List<T> _ObjectList = new List<T>();
    private bool _bDisposed = false;

    /// <summary>
    /// object counter
    /// </summary>
    public int Count
    {
        get { return _ObjectList.Count; }
    }
    /// <summary>
    /// indexer for direct index based access (e.g. mylist[1])
    /// </summary>
    /// <param name="pos"></param>
    /// <returns></returns>
    public T this[int pos]
    {
        get { return _ObjectList[pos]; }
        set { _ObjectList[pos] = value; }
    }

    public GenericList()
    {
    }

    // destructor, makro for 'protected override void Finalize()'
    ~GenericList()
    {
        this.Dispose(false);
    }

    // public Dispose-method for cleanup
    public void Dispose()
    {
        this.Dispose(true);
    }

    // internal Dispose-method
    private void Dispose(bool bDisposing)
    {
        if (!_bDisposed)
        {
            if (bDisposing)
            {
                // e.g. free managed resources here
            }
        }

        _bDisposed = true;
    }

    /// <summary>
    /// interface function for IEnumerable and IEnumerator
    /// </summary>
    /// <returns></returns>
    public IEnumerator GetEnumerator()
    {
        return new GenericListEnumerator(this);
    }
    /// <summary>
    /// add object to list
    /// </summary>
    /// <param name="obj">your object</param>
    public void Add(T obj)
    {
        _ObjectList.Add(obj);
    }
    /// <summary>
    /// clear list
    /// </summary>
    public void Clear()
    {
        _ObjectList.Clear();
    }
    /// <summary>
    /// swap objects inside list
    /// </summary>
    /// <param name="index1">index number one</param>
    /// <param name="index2">index number one</param>
    public void Swap(int index1, int index2)
    {
        if ((index1 >= 0) && (index2 >= 0) && (index1 < _ObjectList.Count) && (index2 < _ObjectList.Count))
        {
            T temp = _ObjectList[index1];
            _ObjectList[index1] = _ObjectList[index2];
            _ObjectList[index2] = temp;
        }
    }
    /// <summary>
    /// remove object from list
    /// </summary>
    /// <param name="obj"></param>
    public void Remove(T obj)
    {
        _ObjectList.Remove(obj);
    }
}

Fließkommazahl kulturunabhängig in einen String wandeln

Speichert man Fließkommazahlen auf unterschiedlichen Systemen als String ab, kommt es vor, dass diese kulturabhängig formatiert werden. Beim Parsen solcher Strings treten dann Fehler auf, weil das System z.B. nicht mit dem Decimalseparator klarkommt.

Das Problem kann man umgehen, indem man die Strings mittels CultureInfo.InvariantCulture umwandelt:

double v = 3.14;

string value = v.ToString(CultureInfo.InvariantCulture); // value == "3.14"

Das Einlesen muss dann ebenfalls über CultureInfo.InvariantCulture erfolgen:

string value = "3.14";
double dValue = 0.0;
if (double.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out dValue))
{
    ...
}

Enum DescriptionAttribute auslesen

using System;
using System.ComponentModel;
using System.Reflection;

/// <summary->
/// freeware helper class for getting enum descriptions
/// (W) 2011 by admin of codezentrale.6x.to
/// </summary->
public static class EnumDescriptor
{
    /// <summary->
    /// get enum attribute description
    /// </summary->
    /// <param name=&quot;val&quot;->current enum</param->
    /// <returns->returns enum attribute description</returns->
    public static string GetDescription(Enum val)
    {
        FieldInfo fi = val.GetType().GetField(val.ToString());
        DescriptionAttribute[] attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);

        return (attributes.Length -> 0) ? attributes[0].Description : val.ToString();
    }
}

Enum benennen und Beschreibung eines Enum in einer ComboBox ausgeben

Machmal kommt es vor, dass man die Werte eines Enumerators als String darstellen will. In der Regel wird vom Framework bei der Verwendung von ToString() einfach der deklarierte Name des Wertes ausgegeben. Möchte man nun, dass Anstelle des Namens eine eigene Definition angezeigt wird, könnte man auf die Idee kommen ToString() zu überschreiben. Das funktioniert aber nicht. Stattdessen muss man auf sog. DescriptionAttribute zurückgreifen.

using System.Reflection;
using System.ComponentModel;

public enum ESortOrder
{
    [Description("aufsteigend")] // Beschreibungstext für Asc
    Asc,
    [Description("absteigend")] // Beschreibungstext für Desc
    Desc
}

cbSortOrder.Items.Clear();

// alle Elemente des ESortOrder-Enums durchgehen und zugehörige
// Description ("aufsteigend", "absteigend") herausfiltern und
// in der Combobox "cbSortOrder" ausgeben
// [Description( ... )] muss für jedes Enum definiert sein,
// sonst Exception beim Zugriff auf attributes[0]
foreach (ESortOrder val in Enum.GetValues(typeof(ESortOrder)))
{
    FieldInfo fi = val.GetType().GetField(val.ToString());
    DescriptionAttribute[] attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);

    cbSortOrder.Items.Add(attributes[0].Description);
}

cbSortOrder.SelectedIndex = 0;
  • Weiterführendes Beispiel: Link

Generische, XML-serialisierbare Parameterklasse

Objektdefinitionen

/// <summary>
/// Beispielenumerator
/// </summary> 
public enum ESType
{
    None,
    Static,
    Slope
}
/// <summary>
/// Beispielincludes, diese Typen werden hiermit importiert und können später per XML serialisiert werden
/// muss um die gewünschten Typen erweitert werden
/// </summary> 
[XmlInclude(typeof(GenericDataDescriptor<int>))]
[XmlInclude(typeof(GenericDataDescriptor<int[]>))]
[XmlInclude(typeof(GenericDataDescriptor<uint>))]
[XmlInclude(typeof(GenericDataDescriptor<uint[]>))]
[XmlInclude(typeof(GenericDataDescriptor<float>))]
[XmlInclude(typeof(GenericDataDescriptor<float[]>))]
[XmlInclude(typeof(GenericDataDescriptor<double>))]
[XmlInclude(typeof(GenericDataDescriptor<double[]>))]
[XmlInclude(typeof(GenericDataDescriptor<bool>))]
[XmlInclude(typeof(GenericDataDescriptor<bool[]>))]
[XmlInclude(typeof(GenericDataDescriptor<string>))]
[XmlInclude(typeof(GenericDataDescriptor<string[]>))]
[XmlInclude(typeof(GenericDataDescriptor<byte>))]
[XmlInclude(typeof(GenericDataDescriptor<byte[]>))]
[XmlInclude(typeof(GenericDataDescriptor<List<int>>))]
[XmlInclude(typeof(GenericDataDescriptor<List<uint>>))]
[XmlInclude(typeof(GenericDataDescriptor<List<float>>))]
[XmlInclude(typeof(GenericDataDescriptor<List<double>>))]
[XmlInclude(typeof(GenericDataDescriptor<List<bool>>))]
[XmlInclude(typeof(GenericDataDescriptor<List<string>>))]
[XmlInclude(typeof(GenericDataDescriptor<List<byte>>))]
[XmlInclude(typeof(CBlockParam<ESType>))]
/// <summary>
/// Dummyobjekt für die ParameterListen der Blöcke
/// </summary> 
public class CBlockParamBase
{
    private string _sName = string.Empty;
    private bool _bChangeableByUser = false;
    protected object _oValue = null;

    [XmlAttribute("name")]
    public string Name
    {
        get { return _sName; }
        set { _sName = value; }
    }
    [XmlAttribute("changeable")]
    public bool ChangeableByUser
    {
        get { return _bChangeableByUser; }
        set { _bChangeableByUser = value; }
    }
    [XmlIgnore]
    public object oValue
    {
        get { return _oValue; }
        set { _oValue = value; }
    }
    [XmlIgnore]
    public virtual Type ParamType
    {
        // dummy
        get { return typeof(int); }
    }

    public CBlockParamBase()
    {
    }
}
/// <summary>
/// generisches Parameterobjekt
/// </summary>
/// <typeparam name="T">Wert- oder Referenztyp des Parameters</typeparam>
public class CBlockParam<T> : CBlockParamBase
{
    #region Properties
    [XmlElement("value")]
    public T tValue
    {
        get { return (T)_oValue; }
        set { _oValue = (object)value; }
    }
    [XmlIgnore]
    public override Type ParamType
    {
        get { return typeof(T); }
    }
    #endregion

    public CBlockParam()
    {
        // muss leer bleiben, sonst Fehler beim Laden von XML (deserialize)
    }

    public CBlockParam(string sName, T Value, bool bIsUserCangeable)
    {
        this.Name = sName;
        _oValue = (object)Value;
        this.ChangeableByUser = bIsUserCangeable;
    }
}

Verwendung

// Parameterliste für alle Parameter vom Typ des allg. Interfaces
private List<CBlockParam> ParameterListe = new List<CBlockParam>();

// ein paar Parameterdefinition
CBlockParam<int> Integerwert = new CBlockParam<int>("Parameter integer", 0, true);
CBlockParam<bool> Boolwert = new CBlockParam<bool>("Parameter bool", true, true);
CBlockParam<double> Doublewert = new CBlockParam<double>("Parameter double", 0.0, true);
CBlockParam<string> Stringwert = new CBlockParam<string>("Parameter string", "x1+x2", true);
CBlockParam<ESType> Enumwert = new CBlockParam<ESType>("Parameter enum", ESType.Slope, true);
CBlockParam<List<int>> ListObject = new CBlockParam<List<int>>("Parameter object", new List<int>, false);

// die Parameter der Liste hinzufügen
ParameterListe.Add(Integerwert);
ParameterListe.Add(Boolwert);
ParameterListe.Add(Doublewert);
ParameterListe.Add(Stringwert);
ParameterListe.Add(Enumwert);
ParameterListe.Add(ListObject);

// alle Parameter der Liste ausgeben
foreach (CBlockParamBase bp in ParameterListe)
{
    if (bp.ChangeableByUser)
    {
        Console.WriteLine(bp.Name + ": " + bp.oValue.ToString());
    }
}
                
// Parametertypen unterscheiden
// beispielsweise den ersten Parameter aus der Liste holen
CBlockParamBase bpb = ParameterListe[0] as CBlockParamBase;
// anhand des Typs den TypeCode ermitteln und per TypeCode
// eine Unterscheidung vornehmen, hier beispielhaft für die Typen
// bool, double, string, int, enum, object [List]
switch (Type.GetTypeCode(bpb.ParamType))
{
    case TypeCode.Boolean:
        break;
    case TypeCode.Double:
        break;
    case TypeCode.String:
        break;
    case TypeCode.Int32:
        CBlockParam<int> bpi = bpb as CBlockParam<int>;
        if (bpi != null)
        {
            // int
        }
        else
        {
            // enum
            foreach (string sTypeName in Enum.GetNames(bpb.ParamType))
            {
                Console.WriteLine(sTypeName);
            }
        }
        break;
    case TypeCode.Object:
        // list
        CBlockParam<List<int>> l = bpb as CBlockParam<List<int>>;
        if (l != null)
        {
        }
        break;
    default:
        break;
}

// Beispiele für Wertzuweisungen
// 1. allgemein über object
// 2. speziell über Typ-spezifizierten Wert
Integerwert.oValue = 24;
Integerwert.tValue = 24;
Boolwert.oValue = true;
Boolwert.tValue = true;
Doublewert.oValue = 10.0;
Doublewert.tValue = 10.0;
string sFormel = (string)Stringwert.oValue;
string sFormel = Stringwert.tValue;
ESType eTest = (ESType)Enumwert.oValue;
ESType eTest = Enumwert.tValue;
int b = ListObject.tValue[0];
int b = ((List<int>)ListObject.oValue)[0];

switch … case mit typeof

// value ist ein bel. Objekt
// es können allerdings nur die in TypeCode
// definierten Systemtypen behandelt werden
switch (Type.GetTypeCode(value.GetType()))
{
    case TypeCode.Boolean :
        break;
    case TypeCode.Char :
        break;
    case TypeCode.String :
        break;
    case TypeCode.DateTime :
        break;
    case TypeCode.Object :
        break;
    default :
        break;
}