-----------------------------------

Acquista i software ArcGIS tramite Studio A&T srl, rivenditore autorizzato dei prodotti Esri.

I migliori software GIS, il miglior supporto tecnico!

I migliori software GIS, il miglior supporto tecnico!
Azienda operante nel settore GIS dal 2001, specializzata nell’utilizzo della tecnologia ArcGIS e aderente ai programmi Esri Italia Business Network ed Esri Partner Network

-----------------------------------



mercoledì 30 novembre 2011

Deserializzazione JSON: polimorfismo

Quando si ha a che fare con il formato JSON, ad esempio quando richiamiamo i servizi rest SOE ESRI, .NET ci mette a disposizione DataContractJsonSerializer che ci consente di serializzare direttamente oggetti di tipo .NET in dati JSON e di deserializzare tali dati in istanze di tipi .NET. Tuttavia, uno dei problemi sorge quando il servizio REST ci restituisce istanze di oggetti diversi o derivati. Javascript è un linguaggio con tipizzazione debole e l'identità del tipo non rappresenta solitamente un problema. Il problema si presenta quando si utilizza JSON per le comunicazioni tra un sistema fortemente tipizzato (.NET) e uno con tipizzazione debole (Javascript): è utile allora mantenere l'identità del tipo.

Prendiamo questo esempio: i tipi con nomi di contratto dati "Envelope" e "Point" derivano da un tipo con nome di contratto dati "Geometry". Se "Point" viene inviato da .NET a Javascript e viene quindi restituito ad un metodo .NET che prevede "Geometry", è utile per .NET sapere che l'oggetto in questione è stato originariamente un "Point"; in caso contrario, tutte le informazioni specifiche del tipo derivato (ad esempio i membri dati "x" e "y" su "Point") potrebbero andare perdute.
Per mantenere l'identità del tipo, durante la serializzazione di tipi complessi in JSON, può essere aggiunto un suggerimento; il deserializzatore riconosce tale suggerimento e agisce di conseguenza. Il suggerimento relativo ai tipi è rappresentato da una coppia chiave/valore JSON dove il nome della chiave è "__type" (due caratteri di sottolineatura seguiti dalla parola "type"). Il valore è rappresentato invece da una stringa JSON con forma 'DataContractName:DataContractNamespace' (tutto il testo che precede i due punti rappresenta il nome).

Per ridurre le dimensioni dei messaggi JSON, il prefisso dello spazio dei nomi del contratto dati predefinito viene sostituito dal carattere "#". Per rendere reversibile questa sostituzione, viene utilizzata una regola di escape: se il primo carattere nello spazio dei nomi è "#" o "\", verrà aggiunto un carattere "\"). Pertanto, se "Point" è un tipo nello spazio dei nomi .NET "Studioat.Geometry", il relativo spazio dei nomi del contratto dati sarà: http://schemas.datacontract.org/2004/07/Studioat.Geometry. Le forme e la rappresentazione JSON appaiono nel modo seguente.

//prima forma
string a1 = "{\"__type\":\"Point:http:\\/\\/schemas.datacontract.org\\/2004\\/07\\/Studioat.Geometry\",\"X\":10.256,\"Y\":80.729}";
//seconda forma
string a2 = "{\"__type\":\"Point:#Studioat.Geometry\",\"X\":10.256,\"Y\":80.729}";

Sia i nomi troncati (#Studioat.Geometry) che quelli completi vengono riconosciuti durante la deserializzazione. Inoltre il suggerimento relativo ai tipi deve essere visualizzato all'inizio nella rappresentazione JSON. Questo è il solo caso in cui l'ordine delle coppie chiave/valore è importante nell'elaborazione di JSON.
Di seguito viene riportato un esempio di modalità non valida per specificare un suggerimento relativo ai tipi.
string a2 = "{\"X\":10.256,\"Y\":80.729,\"__type\":\"Point:#Studioat.Geometry\"}";



using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
 
namespace Studioat.ScratchCode
{
 class Program
 {
  static void Main(string[] args)
  {
     string a1 = "{\"__type\":\"Point:http:\\/\\/schemas.datacontract.org\\/2004\\/07\\/Studioat.Geometry\",\"X\":10.256,\"Y\":80.729}";
     string a2 = "{\"__type\":\"Point:#Studioat.Geometry\",\"X\":10.256,\"Y\":80.729}";
 
     string b1 = "{\"__type\":\"Envelope:http:\\/\\/schemas.datacontract.org\\/2004\\/07\\/Studioat.Geometry\",\"XMin\":10.256,\"YMin\":80.729,\"XMax\":21.956,\"YMax\":34.712}";
     string b2 = "{\"__type\":\"Envelope:#Studioat.Geometry\",\"XMin\":10.256,\"YMin\":80.729,\"XMax\":21.956,\"YMax\":34.712}";
 
     byte[] rawstring = System.Text.Encoding.Unicode.GetBytes(a1);
     MemoryStream stream = new MemoryStream(rawstring);
 
     DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(Geometry.Geometry), new Type[] { typeof(Geometry.Envelope), typeof(Geometry.Point) });
 
     Geometry.Geometry o = serializer.ReadObject(stream) as Geometry.Geometry;
     if (o is Geometry.Point)
     {
      Geometry.Point j = o as Geometry.Point;
      Console.WriteLine("Point x:{0} y:{1}", j.X, j.Y);
     }
     else if (o is Geometry.Envelope)
     {
      Geometry.Envelope j = o as Geometry.Envelope;
      Console.WriteLine("Envelope xmin:{0} ymin:{1} xmax:{2} ymax:{3}", j.XMin, j.YMin, j.XMax, j.YMax);
     }
     Console.Read();
  }
 }
}
 
namespace Studioat.Geometry
{
 [DataContract]
 public abstract class Geometry : ICloneable
 {
    
  protected Geometry()
  {
  }
 
  public abstract object Clone();
 }
 
 [DataContract]
 public class Point : Geometry
 {
 
  private double x;
  private double y;
 
 
  public Point()
  {
   this.x = double.NaN;
   this.y = double.NaN;
 
  }
 
  public Point(double x, double y)
  {
   this.x = x;
   this.y = y;
  }
 
  public override object Clone()
  {
   Studioat.Geometry.Point point = base.MemberwiseClone() as Studioat.Geometry.Point;
   return point;
  }
 
  [DataMember]
  public double X
  {
   get;
   set;
  }
 
  [DataMember]
  public double Y
  {
   get;
   set;
  }
 }
 
 [DataContract]
 public class Envelope : Geometry
 {
 
  private double maxx;
  private double maxy;
  private double minx;
  private double miny;
 
  public Envelope()
   : this(double.NaN, double.NaN, double.NaN, double.NaN)
  {
  }
 
  public Envelope(Point minPoint, Point maxPoint)
   : this(minPoint.X, minPoint.Y, maxPoint.X, maxPoint.Y)
  {   
  }
 
  public Envelope(double minx, double miny, double maxx, double maxy)
  {
   this.minx = minx;
   this.miny = miny;
   this.maxx = maxx;
   this.maxy = maxy;
  }
 
  public override object Clone()
  {
   Envelope envelope = base.MemberwiseClone() as Envelope;
   return envelope;
  }
 
  [DataMember]
  public double XMax
  {
   get;
   set;
  }
 
  [DataMember]
  public double XMin
  {
   get;
   set;
  }
 
  [DataMember]
  public double YMax
  {
   get;
   set;
  }
 
  [DataMember]
  public double YMin
  {
   get;
   set;
  }
 }
}