38

Json.Net を使用して IPEndpoint オブジェクトをシリアル化しようとすると、次のエラーが発生します。

「System.Net.IPAddress」の「ScopeId」から値を取得中にエラーが発生しました。

エラーの原因は、エンドポイントで IPAddress オブジェクトの IPV4 プロパティのみを使用しているためです。Json パーサーが IPv6 部分を解析しようとすると、ScopeID プロパティにアクセスしてソケット例外をスローします。

すべてをバラバラにしてアドレス情報を文字列としてコーディングする以外に、これに対する回避策があるかどうか疑問に思っていましたか? ある時点で、IPV6 をサポートしたいと考えています。IPAddress ファミリが InternetworkIPV6 ではなく Internetwork に設定されている場合、Json.NET でエラーを無視するか、単に ScopeID のシリアル化を試行しないようにできることはありますか?

ありがとう、

ディンズデール

4

1 に答える 1

81

見てきたように、このIPAddressクラスはシリアル化にあまり適していません。IPv4 アドレスSocketExceptionのフィールドにアクセスしようとするとスローされるだけでなく、IPv6 アドレスのフィールドに直接アクセスしようとするとスローされます。 ScopeIDAddress

例外を回避するには、カスタムが必要です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

于 2013-09-07T03:48:05.777 に答える