2013-09-07 12 views
24

Ich versuche, ein IPEndPoint Objekt mit Json.Net zu serialisiert und ich erhalte den folgenden Fehler:Json.Net - Fehlerwert von immer ‚ScopeId‘ auf ‚System.Net.IPAddress‘

Fehler beim Abrufen der Wert von ' ScopeId 'auf' System.Net.IPAddress '.

Die Ursache des Fehlers besteht darin, dass ich nur die IPV4-Eigenschaften des IPAddress-Objekts im Endpunkt verwende. Wenn der Json-Parser versucht, den IPv6-Teil zu analysieren, greift er auf die ScopeID-Eigenschaft zu, die eine Socket-Ausnahme auslöst. "Die versuchte Operation wird für den Objekttyp nicht unterstützt."

ich war frage mich, ob es einen Workaround dafür geben könnte, anstatt alles auseinander zu reißen und die Adressinformationen als String zu codieren? Irgendwann möchte ich IPV6 unterstützen. Gibt es etwas, das in Json.NET getan werden kann, um den Fehler zu ignorieren oder einfach NICHT zu versuchen, die ScopeID zu serialisieren, wenn die IPAddress-Familie auf Internetwork statt auf InternetworkIPV6 eingestellt ist?

Danke,

Dinsdale

Antwort

45

Die IPAddress Klasse ist nicht sehr freundlich zu Serialisierung, wie Sie gesehen haben. Wenn Sie versuchen, auf das Feld ScopeID für eine IPv4-Adresse zuzugreifen, wird es nicht nur SocketException werfen, sondern es wird auch ausgelöst, wenn Sie versuchen, auf das Feld Address direkt für eine IPv6-Adresse zuzugreifen.

Um die Ausnahmen zu umgehen, benötigen Sie eine benutzerdefinierte JsonConverter. Mit einem Konverter können Sie Json.Net genau sagen, wie Sie einen bestimmten Objekttyp serialisieren und/oder deserialisieren möchten. Für einen scheint es der einfachste Weg zu sein, die Daten, die alle befriedigen, einfach in ihre Zeichenfolgendarstellung und zurück zu konvertieren. Das können wir im Konverter machen. Hier ist, wie ich es schreiben würde:

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

Ziemlich einfach, wie diese Dinge gehen. Aber das ist nicht das Ende der Geschichte. Wenn Sie mit Ihrer IPEndPoint eine Hin- und Rückfahrt machen müssen, benötigen Sie auch einen Konverter. Warum? Da IPEndPoint keinen Standardkonstruktor enthält, wird Json.Net nicht wissen, wie es instanziiert wird. Glücklicherweise ist dieser Konverter auch nicht schwer zu schreiben:

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

Also, jetzt, wo wir die Konverter haben, wie benutzen wir sie? Hier ist ein einfaches Beispielprogramm, das demonstriert. Es erstellt zunächst ein paar Endpunkte, serialisiert sie JSON die benutzerdefinierten Konvertern, dann sofort deserialisiert die JSON zurück in Endpunkte wieder die gleichen Wandler verwenden. Hier

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

ist die Ausgabe:

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

+0

Code of 'WriteJson' vereinfacht werden kann' JObject' zu verwenden. – Athari

+0

Brilliant. Vielen Dank Brian! – Dinsdale

+0

Kein Problem; froh, dass ich Helfen kann. –