Подтвердить что ты не робот

Json.Net - Ошибка получения значения из "ScopeId" в "System.Net.IPAddress"

Я пытаюсь сериализовать объект IPEndpoint с Json.Net, и я получаю следующую ошибку:

Ошибка получения значения из "ScopeId" в "System.Net.IPAddress".

Причиной ошибки является то, что я использую только свойства IPV4 объекта IPAddress в конечной точке. Когда парсер Json пытается проанализировать часть IPv6, он обращается к свойству ScopeID, которое выдает исключение сокета. "Проведенная операция не поддерживается для типа объекта, на который делается ссылка" (нуль будет достаточным для Microsoft!)

Мне было интересно, может ли быть обходное решение для этого, кроме разрыва всего кода и кодирования адресной информации в виде строки? В какой-то момент я хочу поддержать IPV6. Есть ли что-нибудь, что можно сделать в Json.NET, чтобы игнорировать ошибку или просто НЕ пытаться сериализовать ScopeID, если для семейства IPAddress установлено значение Internetwork вместо InternetworkIPV6?

Спасибо,

Динсдейл

4b9b3361

Ответ 1

Класс IPAddress не очень дружелюбен к сериализации, как вы видели. Он не только выкинет SocketException если вы попытаетесь получить доступ к полю ScopeID для IPv4-адреса, но он также будет SocketException если вы попытаетесь получить доступ к полю " Address непосредственно для адреса IPv6.

Чтобы обойти исключения, вам понадобится пользовательский JsonConverter. Конвертер позволяет вам точно рассказать Json.Net о том, как вы хотите его сериализовать и/или десериализовать определенный тип объекта. Для IPAddress кажется, что самый простой способ получить данные, которые удовлетворяют всех, - это просто преобразовать его в его строковое представление и обратно. Мы можем сделать это в конвертере. Вот как я бы это написал:

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

Довольно просто, так как все идет. Но это еще не конец истории. Если вам нужно пойти туда и обратно с помощью IPEndPoint, вам также понадобится конвертер. Зачем? Поскольку IPEndPoint не содержит конструктор по умолчанию, поэтому Json.Net не будет знать, как его создать. К счастью, этот конвертер не составит труда написать:

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

Итак, теперь, когда у нас есть конвертеры, как мы их используем? Вот простая примерная программа, которая демонстрирует. Сначала он создает пару конечных точек, сериализует их в JSON с использованием пользовательских преобразователей, а затем снова десериализует JSON обратно в конечные точки снова с использованием тех же конвертеров.

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

Вот результат:

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

Скрипт: https://dotnetfiddle.net/tK7NKY