2013-09-07 18 views
24

Próbuję serializacji obiektu IPEndpoint z Json.NET i pojawia się następujący błąd:Json.Net - Błąd pobierania wartości z „wartości ScopeId” on „System.Net.IPAddress”

Błąd pobierania wartości z ' ScopeId "on" System.Net.IPAddress ".

Przyczyną błędu jest to, że używam tylko właściwości IPV4 obiektu IPAddress w punkcie końcowym. Gdy parser Json próbuje przeanalizować część IPv6, uzyskuje dostęp do właściwości ScopeID, która zgłasza wyjątek gniazda "Podjęta operacja nie jest obsługiwana dla typu obiektu, do którego się odwołuje" (Null byłaby wystarczająca dla Microsoftu!)

Byłem Zastanawiasz się, czy może istnieć obejście tego problemu, zamiast zgrywania wszystkiego na części i kodowania informacji adresowych jako ciągu? W pewnym momencie chcę wspierać IPV6. Czy jest coś, co można zrobić w Json.NET, aby zignorować błąd lub po prostu NIE próbować serializować identyfikatora ScopeID, jeśli rodzina IPAddress jest ustawiona na Internetwork zamiast InternetworkIPV6?

Dzięki,

Dinsdale

Odpowiedz

45

Klasa IPAddress nie jest bardzo przyjazny dla serializacji, jak widzieliście. Nie tylko rzuci SocketException, jeśli spróbujesz uzyskać dostęp do pola ScopeID dla adresu IPv4, ale będzie również rzutować, jeśli spróbujesz uzyskać dostęp do pola Address bezpośrednio dla adresu IPv6.

Aby obejść wyjątki, potrzebny będzie niestandardowy JsonConverter. Konwerter pozwala ci powiedzieć Json.Net dokładnie, jak chcesz, aby serializować i/lub deserializować konkretny typ obiektu. Dla modelu IPAddress wydaje się, że najłatwiejszym sposobem uzyskania danych, które zadowolą wszystkich, jest po prostu przekształcenie go w jego reprezentację ciągów i powrót. Możemy to zrobić w konwerterze. Oto, jak to napisać:

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

Dość proste, jak te rzeczy. Ale to nie koniec historii. Jeśli chcesz jeździć w obie strony razem z urządzeniem IPEndPoint, potrzebujesz również konwertera. Czemu? Ponieważ IPEndPoint nie zawiera domyślnego konstruktora, więc Json.Net nie będzie wiedział, jak go utworzyć. Na szczęście ten konwerter również nie jest trudny do napisania:

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

Więc, skoro mamy konwertery, jak ich używamy? Oto prosty przykładowy program, który pokazuje. Najpierw tworzy kilka punktów końcowych, serializuje je do JSON-a za pomocą niestandardowych konwerterów, a następnie ponownie deserializuje JSON z powrotem do punktów końcowych ponownie, używając tych samych konwerterów.

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

Oto wynik:

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

Kodeks 'WriteJson' można uprościć używając' JObject' też. – Athari

+0

Brilliant. Dziękuję bardzo Brian! – Dinsdale

+0

Bez problemu; miło, że mogłem pomóc. –

Powiązane problemy