web-dev-qa-db-fra.com

JSON.NET - Erreur lors de l'obtention de la valeur de "ScopeID" sur 'system.net.ipaddress "

J'essaie de sérialiser un objet iPendPoint avec json.net et je reçois l'erreur suivante:

Erreur d'obtention de la valeur de "ScopeID" sur 'System.net.ipaddress ".

La cause de l'erreur est que je n'utilise que les propriétés IPv4 de l'objet iPaddress dans le noeud final. Lorsque l'analyseur JSON tente d'analyser la partie IPv6, il accède à la propriété ScopeID qui jette une exception à socket "La tentative d'opération n'est pas prise en charge pour le type d'objet référencé" (une null aurait suffi à Microsoft!)

Je me demandais s'il y a eu une solution de contournement pour cela autre que de déchirer tout et codez les informations d'adresse comme une chaîne? À un moment donné, je veux soutenir IPv6. Y a-t-il quelque chose qui peut être fait dans json.net pour ignorer l'erreur ou tout simplement pas tenter de sérialiser la portée si la famille iPaddress est définie sur internetwork au lieu d'InternetWorkIPV6?

Merci,

Dinsdale

33
Dinsdale

La classe IPAddress n'est pas très amicale de sérialiser, comme vous l'avez vu. Non seulement il lancera un SocketException si vous essayez d'accéder au champ ScopeID pour une adresse IPv4, mais il va également lancer si vous essayez d'accéder directement au champ Address directement pour une adresse IPv6.

Pour contourner les exceptions, vous aurez besoin d'une personnalisation JsonConverter. Un convertisseur vous permet de dire à JSON.net exactement comment vous aimeriez qu'il soit sérialisé et/ou désorienciez un type d'objet particulier. Pour un IPAddress, il semble que le moyen le plus simple d'obtenir les données qui satisfont à tout le monde est simplement de le convertir à sa représentation de chaîne et à son retour. Nous pouvons le faire dans le convertisseur. Voici comment je l'écrirais:

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);
    }
}

Assez simple, comme ces choses vont. Mais ce n'est pas la fin de l'histoire. Si vous devez aller aller-retour avec votre IPEndPoint, vous aurez également besoin d'un convertisseur. Pourquoi? Parce que IPEndPoint ne contient pas de constructeur par défaut, JSON.net ne saura pas comment l'instancier. Heureusement, ce convertisseur n'est pas difficile à écrire non plus:

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);
    }
}

Alors, maintenant que nous avons les convertisseurs, comment les utilisons-nous? Voici un exemple simple qui démontre. Il crée d'abord quelques points de terminaison, les sérialise à JSON à l'aide des convertisseurs personnalisés, puis désériorialise immédiatement le JSON dans les points d'extrémité à nouveau à l'aide des mêmes convertisseurs.

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);
        }
    }
}

Voici la sortie:

[
  {
    "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

60
Brian Rogers