Press "Enter" to skip to content

JSON e la serializzazione delle Enumerazioni

Al solito faccio un post per non dimenticarmi e per trovare rapidamente tutto questo quando mi succederà di nuovo.

Come da titolo, questo è un Tip sulla serializzazione JSON da C#, ed in particolare riguardo la serializzazione di valori enumerati.

Per i Beginner: cosa sono le Enumerazioni? Si tratta semplicemente di un modo per dare un nome strutturato ad un valore numerico da utilizzare nel codice per poter fare in modo semplice delle istruzioni di controllo del flusso semplificandoci la vita evitando l’uso delle stringhe.

	public enum Fruits
	{
		NotSet,
		Apples,
		Oranges,
		Strawberries,
		Lemons,
		RedBerries,
		BlueBerries
	}

Qui sopra vediamo un enumerazione, che usata nel codice ci permette di fare cose come:

private void FruitCycle( List<FriendlyFruit> fruits)
{

	foreach (FriendlyFruit fruit in fruits)
	{
		switch (fruit.Type)
		{
			case Fruits.Apples:
				Console.WriteLine("This is an apple");
				Console.WriteLine(fruit.ToJson(true));
				break;
			case Fruits.BlueBerries:
				Console.WriteLine("This is a blueberry");
				Console.WriteLine(fruit.ToJson(true));
				break;
			//And so on
		}

                if( fruit.Type == Fruits.Strawberry )
                {
                    Console.Writeline("Attention! some people can be allergic.");
                }
	}

        

}

Ovviamente si tratta di un esempio, gli switch o gli if sulle enumerazioni servono a semplificarci la vita e ad evitare gli errori che potrebbero accadere se usassimo delle stringhe o la difficoltà di utilizzare dei numeri per lo stesso lavoro.

E veniamo al JSON e al motivo di questo articolo. JSON è un sistema per la descrizione di oggetti creato per essere per quanto possibile  conciso, perché usato sul WEB dove meno è meglio, al contrario del molto più verboso XML, pertanto ha tutta una serie di accorgimenti per rendere i dati compatti. Una delle cose che lo standard JSON fa è convertire automaticamente le enumerazioni nel loro valore numerico. Pertanto, l’output del seguente codice:

 

StandardFruit standardFruit = new StandardFruit()
{
	Name = "Arancia",
	Color = "Arancione",
	Type = Fruits.Oranges
};

string standardJSON = standardFruit.ToJson(true);
Console.WriteLine("** Standard Json Fruit **");
Console.WriteLine(standardJSON);
Console.WriteLine(new string('-', 80));

Da origine al seguente output su console:

** Standard Json Fruit **
{
  "Name": "Arancia",
  "Color": "Arancione",
  "Type": 2
}

Questo perché i valori enumerati se non indicato diversamente partono da 0 con incremento di 1 pertanto Oranges vale 2.

Ovviamente far vedere il numero 2 su una lista HTML può essere di poco aiuto all’utente, inoltre se per caso qualcuno modifica l’enumerazione e invece di aggiungere un dato in fondo lo inserisce in mezzo, i valori cambiano e gli utenti presumo si arrabbierebbero.

Certamente chi fa la parte web potrebbe convertire i numeri in stringhe lato client, però resterebbe il problema dovuto al fatto che qualcuno potrebbe cambiare le posizioni nell’enumerazione, magari mesi dopo che è stato fatto il codice e provocare dei gravi problemi.

Ecco perché è opportuno che il valore passato dal JSON sia la stringa e non il numero. Per fare la trasformazione il codice è molto semplice:

 

 

FriendlyFruit friendlyFruit = new FriendlyFruit()
{
	Name = "Mirtillo",
	Color = "Blu Scuro",
	Type = Fruits.BlueBerries
};

friendlyJSON = friendlyFruit.ToJson(true);
Console.WriteLine("** Friendly Json Fruit **");
Console.WriteLine(friendlyJSON);
Console.WriteLine(new string('-', 80));


//La property Type va modificata così:
private Fruits mType;

[JsonConverter(typeof(Conv.StringEnumConverter))]
public Fruits Type
{
	get
	{
		return mType;
	}
	set
	{
		mType = value;
	}
}

La classe FrendlyFruit è identica alla classe StandardFruit, ma sulla sua property Type abbiamo aggiunto un converter standard di Newtonsoft.json che effettua la trasformazione, quindi il risultato è:

** Friendly Json Fruit **
{
  "Name": "Mirtillo",
  "Color": "Blu Scuro",
  "Type": "BlueBerries"
}

L’importante è ricordarsi di utilizzare la serializzazione di Newtonsoft.json e non quella standard del framework .net perché la seconda si comporta in modo non “standard” metto la parola fra virgolette perché lo standard è relativo a come si comportano buona parte dei sistemi di serializzazione usati in internet, non perché le classi del framework siano in qualche modo sbagliate. Solo che il risultato a volte non è quello atteso da Javascript e quindi le maledizioni senza perdono potrebbero abbattersi su noi programmatori lato Backweb.

Aggiungo il codice della mini applicazione console che ho usato per la dimostrazione.

using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Conv = Newtonsoft.Json.Converters;
namespace EnumJson
{
	public enum Fruits
	{
		NotSet,
		Apples,
		Oranges,
		Strawberries,
		Lemons,
		RedBerries,
		BlueBerries

	}

	class Program
	{
		static void Main(string[] args)
		{
			//Let's produce two fruits
			StandardFruit standardFruit = new StandardFruit()
			{
				Name = "Arancia",
				Color = "Arancione",
				Type = Fruits.Oranges
			};

			FriendlyFruit friendlyFruit = new FriendlyFruit()
			{
				Name = "Mirtillo",
				Color = "Blu Scuro",
				Type = Fruits.BlueBerries
			};

			string standardJSON = standardFruit.ToJson(true);
			Console.WriteLine("** Standard Json Fruit **");
			Console.WriteLine(standardJSON);
			Console.WriteLine(new string('-', 80));

			string friendlyJSON = friendlyFruit.ToJson(true);
			Console.WriteLine("** Friendly Json Fruit **");
			Console.WriteLine(friendlyJSON);
			Console.WriteLine(new string('-', 80));

			standardFruit = StandardFruit.FromJson(friendlyJSON);
			friendlyFruit = FriendlyFruit.FromJson(standardJSON);
			Console.WriteLine("** Exchange of data before deserializing **");

			standardJSON = standardFruit.ToJson(true);
			Console.WriteLine("** Standard Json Fruit **");
			Console.WriteLine(standardJSON);
			Console.WriteLine(new string('-', 80));

			friendlyJSON = friendlyFruit.ToJson(true);
			Console.WriteLine("** Friendly Json Fruit **");
			Console.WriteLine(friendlyJSON);
			Console.WriteLine(new string('-', 80));

			Console.WriteLine("** Looks like Newtonsoft is automatically converting the enumeration, Clever!!! **");

			Console.WriteLine("** Lista di frutti con tipologie **");

			List<FriendlyFruit> fruttivari = new List<FriendlyFruit>();
			fruttivari.Add( new FriendlyFruit() { Color = "Orange", Name = "Albicocca", Type = Fruits.NotSet });
			fruttivari.Add( new FriendlyFruit() { Color = "Green", Name = "Granny Smith", Type = Fruits.Apples });
			fruttivari.Add( new FriendlyFruit() { Color = "Yellow", Name = "Golden", Type = Fruits.Apples });
			fruttivari.Add( new FriendlyFruit() { Color = "Indigo", Name = "Susina", Type = Fruits.NotSet });
			fruttivari.Add( new FriendlyFruit() { Color = "Orange", Name = "Mandarino", Type = Fruits.Oranges });
			fruttivari.Add( new FriendlyFruit() { Color = "Orange", Name = "Washington Navel", Type = Fruits.Oranges });
			fruttivari.Add( new FriendlyFruit() { Color = "Red and Green", Name = "Fuji", Type = Fruits.Apples });
			fruttivari.Add( new FriendlyFruit() { Color = "Red", Name = "Mirtilli rossi", Type = Fruits.RedBerries });
			fruttivari.Add( new FriendlyFruit() { Color = "Indigo", Name = "Mirtilli", Type = Fruits.BlueBerries });

			FruitCycle(fruttivari);
			Console.WriteLine("** Premi enter per terminare **");
			Console.ReadLine();

		}


		private static void FruitCycle( List<FriendlyFruit> fruits)
		{

			foreach (FriendlyFruit fruit in fruits)
			{
				switch (fruit.Type)
				{
					case Fruits.Apples:
						Console.WriteLine("This is an apple");
						Console.WriteLine(fruit.ToJson(true));
						break;
					case Fruits.Oranges:
						Console.WriteLine("This is an orange");
						Console.WriteLine(fruit.ToJson(true));
						break;
					case Fruits.Strawberries:
						Console.WriteLine("This is a strawberry");
						Console.WriteLine(fruit.ToJson(true));
						break;
					case Fruits.Lemons:
						Console.WriteLine("This is a Lemon");
						Console.WriteLine(fruit.ToJson(true));
						break;
					case Fruits.BlueBerries:
						Console.WriteLine("This is a blueberry");
						Console.WriteLine(fruit.ToJson(true));
						break;
					case Fruits.RedBerries:
						Console.WriteLine("This is a redberry");
						Console.WriteLine(fruit.ToJson(true));
						break;
					default:
						Console.WriteLine("Fruit type not set");
						Console.WriteLine(fruit.ToJson(true));
						break;
				}
			}


		}
	}

	class StandardFruit
	{
		private string mName;
		public string Name
		{
			get
			{
				return mName;
			}
			set
			{
				mName = value;
			}
		}

		private string mColor;

		public string Color
		{
			get
			{
				return mColor;
			}
			set
			{
				mColor = value;
			}
		}

		private Fruits mType;
		public Fruits Type
		{
			get
			{
				return mType;
			}
			set
			{
				mType = value;
			}
		}

		public string ToJson(bool indented)
		{
			Formatting formatting = Formatting.None;
			if (indented)
			{
				formatting = Formatting.Indented;
			}
			return JsonConvert.SerializeObject(this, formatting);
		}

		public static StandardFruit FromJson(string jsonData)
		{
			StandardFruit fruit = null;
			try
			{
				fruit = JsonConvert.DeserializeObject<StandardFruit>(jsonData);
			}
			catch (Exception)
			{
				fruit = null;
				//In caso di errore torniamo null
			}
			return fruit;
		}



	}

	class FriendlyFruit
	{

		private string mName;
		public string Name
		{
			get
			{
				return mName;
			}
			set
			{
				mName = value;
			}
		}

		private string mColor;

		public string Color
		{
			get
			{
				return mColor;
			}
			set
			{
				mColor = value;
			}
		}


		private Fruits mType;

		[JsonConverter(typeof(Conv.StringEnumConverter))]
		public Fruits Type
		{
			get
			{
				return mType;
			}
			set
			{
				mType = value;
			}
		}

		public string ToJson(bool indented)
		{
			Formatting formatting = Formatting.None;
			if (indented)
			{
				formatting = Formatting.Indented;
			}
			return JsonConvert.SerializeObject(	this, formatting);
		}

		public static FriendlyFruit FromJson(string jsonData)
		{
			FriendlyFruit fruit = null;
			try
			{
				fruit = JsonConvert.DeserializeObject<FriendlyFruit>(jsonData);
			}
			catch (Exception)
			{
				fruit = null;
				//In caso di errore torniamo null
			}
			return fruit;
		}

	}
}