The IPAddress
class is not very friendly to serialization, as you’ve seen. Not only will it throw a SocketException
if you try to access the ScopeID
field for an IPv4 address, but it will also throw if you try to access the Address
field directly for an IPv6 address.
To get around the exceptions, you will need a custom JsonConverter
. A converter allows you to tell Json.Net exactly how you’d like it to serialize and/or deserialize a particular type of object. For an IPAddress
, it seems the easiest way to get the data that satisfies everyone is simply to convert it to its string representation and back. We can do that in the converter. Here is how I would write it:
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); } }
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); } }
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); } } }
Here is the 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
-
Code of
WriteJson
can be simplified usingJObject
too. -
Performance impact of
WriteJson
andReadJson
can be improved by using the writer and reader objects, avoidingJObject
allocation. I have submitted an edit to this very useful answer.