c# - Json.Net - 从 'ScopeId' 上的 'System.Net.IPAddress' 获取值时出错

标签 c# .net json.net system.net

我正在尝试使用 Json.Net 序列化 IPEndpoint 对象,但出现以下错误:

从“System.Net.IPAddress”上的“ScopeId”获取值时出错。

错误的原因是我只使用端点中 IPAddress 对象的 IPV4 属性。当 Json 解析器尝试解析 IPv6 部分时,它会访问 ScopeID 属性,该属性会引发套接字异常“所引用的对象类型不支持尝试的操作”(null 足以满足微软的要求!)

我想知道除了将所有内容分开并将地址信息编码为字符串之外,是否有解决此问题的方法?在某些时候我确实想支持 IPV6。如果 IPAddress 系列设置为 Internetwork 而不是 InternetworkIPV6,是否可以在 Json.NET 中执行任何操作来忽略错误或干脆不尝试序列化 ScopeID?

谢谢,

丁斯代尔

最佳答案

如您所见,IPAddress 类对序列化不是很友好。如果您尝试访问 IPv4 地址的 ScopeID 字段,它不仅会抛出 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

fiddle :https://dotnetfiddle.net/tK7NKY

关于c# - Json.Net - 从 'ScopeId' 上的 'System.Net.IPAddress' 获取值时出错,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18668617/

相关文章:

c# - Unity Json反/序列化嵌套数据

.net - 将大量静态数据写入代码而不是数据库?

c# - 反序列化不同类型的JSON数组

c# - 使用 C#/Json 序列化 Web 服务中的结构

c# - 使用 C# 和 JSON.net 读取 JSON 文件

c# - 每 2 秒更新一次 WPF GUI (C#)

c# - EWS - 确定电子邮件是回复还是已转发

c# - 使用 UWP C# 的 Hololens 条码读取器

.net - 如何使用 Swashbuckle 在生成的 Swagger 文件中生成全局参数?

c# - MS Access C#.NET 中的 SQL Server