La classe IPAddress
non è molto amichevole per la serializzazione, come avete visto. Non solo getterà un SocketException
se si tenta di accedere al campo ScopeID
per un indirizzo IPv4, ma verrà generato anche se si tenta di accedere al campo Address
direttamente per un indirizzo IPv6.
Per aggirare le eccezioni, è necessario un numero personalizzato JsonConverter
. Un convertitore ti permette di dire a Json.Net esattamente come ti piacerebbe serializzare e/o deserializzare un particolare tipo di oggetto. Per un IPAddress
, sembra che il modo più semplice per ottenere i dati che soddisfano tutti è semplicemente convertirlo alla sua rappresentazione di stringa e viceversa. Possiamo farlo nel convertitore. Ecco come vorrei scriverlo:
class IPAddressConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(IPAddress));
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteValue(value.ToString());
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return IPAddress.Parse((string)reader.Value);
}
}
Piuttosto semplice, come vanno queste cose. Ma questa non è la fine della storia. Se devi fare il viaggio di andata e ritorno con il tuo IPEndPoint
, allora avrai bisogno anche di un convertitore. Perché? Perché IPEndPoint
non contiene un costruttore predefinito, quindi Json.Net non saprà come istanziarlo. Per fortuna, questo convertitore non è difficile scrivere uno:
class IPEndPointConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(IPEndPoint));
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
IPEndPoint ep = (IPEndPoint)value;
JObject jo = new JObject();
jo.Add("Address", JToken.FromObject(ep.Address, serializer));
jo.Add("Port", ep.Port);
jo.WriteTo(writer);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JObject jo = JObject.Load(reader);
IPAddress address = jo["Address"].ToObject<IPAddress>(serializer);
int port = (int)jo["Port"];
return new IPEndPoint(address, port);
}
}
Quindi, ora che abbiamo i convertitori, come possiamo usarli? Ecco un semplice esempio di programma che dimostra. Prima crea un paio di endpoint, li serializza su JSON utilizzando i convertitori personalizzati, quindi deserializza immediatamente nuovamente il JSON negli endpoint utilizzando gli stessi convertitori.
public class Program
{
static void Main(string[] args)
{
var endpoints = new IPEndPoint[]
{
new IPEndPoint(IPAddress.Parse("8.8.4.4"), 53),
new IPEndPoint(IPAddress.Parse("2001:db8::ff00:42:8329"), 81)
};
var settings = new JsonSerializerSettings();
settings.Converters.Add(new IPAddressConverter());
settings.Converters.Add(new IPEndPointConverter());
settings.Formatting = Formatting.Indented;
string json = JsonConvert.SerializeObject(endpoints, settings);
Console.WriteLine(json);
var endpoints2 = JsonConvert.DeserializeObject<IPEndPoint[]>(json, settings);
foreach (IPEndPoint ep in endpoints2)
{
Console.WriteLine();
Console.WriteLine("AddressFamily: " + ep.AddressFamily);
Console.WriteLine("Address: " + ep.Address);
Console.WriteLine("Port: " + ep.Port);
}
}
}
Ecco l'output:
[
{
"Address": "8.8.4.4",
"Port": 53
},
{
"Address": "2001:db8::ff00:42:8329",
"Port": 81
}
]
AddressFamily: InterNetwork
Address: 8.8.4.4
Port: 53
AddressFamily: InterNetworkV6
Address: 2001:db8::ff00:42:8329
Port: 81
Fiddle: https://dotnetfiddle.net/tK7NKY
Codice di 'WriteJson' può essere semplificata utilizzando' JObject' troppo. – Athari
Brillante. Grazie mille Brian! – Dinsdale
Nessun problema; felice di aiutare. –