c# - Json.Net-Error al obtener el valor de ''ScopeId'' en ''System.Net.IPAddress''
(1)
Estoy tratando de serializar un objeto IPEndpoint con Json.Net y me aparece el siguiente error:
Error al obtener el valor de ''ScopeId'' en ''System.Net.IPAddress''.
La causa del error es que solo estoy usando las propiedades IPV4 del objeto IPAddress en el punto final. Cuando el analizador Json intenta analizar la porción IPv6, accede a la propiedad ScopeID que arroja una excepción de socket "La operación intentada no es compatible con el tipo de objeto al que se hace referencia" (¡Un nulo hubiera bastado para Microsoft!)
Me preguntaba si podría haber una solución alternativa para esto aparte de descomponer todo y codificar la información de la dirección como una cadena. En algún momento, quiero apoyar IPV6. ¿Hay algo que se pueda hacer en Json.NET para ignorar el error o simplemente NO intentar serializar el ScopeID si la familia IPAddress está configurada para Internetwork en lugar de internetworkIPV6?
Gracias,
Dinsdale
La clase IPAddress
no es muy amigable con la serialización, como has visto. No solo arrojará una SocketException
si intenta acceder al campo ScopeID
para una dirección IPv4, sino que también lanzará si intenta acceder al campo Address
directamente para una dirección IPv6.
Para evitar las excepciones, necesitará un JsonConverter
personalizado. Un convertidor le permite decirle a Json.Net exactamente cómo le gustaría serializar y / o deserializar un tipo particular de objeto. Para una IPAddress
, parece que la forma más sencilla de obtener los datos que satisfacen a todos es simplemente convertirla a su representación de cadena y viceversa. Podemos hacer eso en el convertidor. Así es como lo escribiría:
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);
}
}
Bastante sencillo, ya que estas cosas van. Pero este no es el final de la historia. Si necesita ir de ida y vuelta con su IPEndPoint
, necesitará un convertidor también. ¿Por qué? Debido a que IPEndPoint
no contiene un constructor predeterminado, Json.Net no sabrá cómo crear una instancia. Afortunadamente, este convertidor no es difícil de escribir:
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);
}
}
Entonces, ahora que tenemos los convertidores, ¿cómo los usamos? Aquí hay un programa de ejemplo simple que demuestra. Primero crea un par de puntos finales, los serializa a JSON utilizando los conversores personalizados, luego inmediatamente deserializa el JSON nuevamente en los puntos finales utilizando los mismos conversores.
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);
}
}
}
Aquí está el resultado:
[
{
"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