2009-09-10 9 views
9

Sto tentando di analizzare alcuni contenuti JSON in C#. Per i casi più semplici sto avendo un grande successo con JSON.NET e apprezzo molto l'approccio pulito offerto dal provider LINQ. Ecco un esempio in cui sto scaricando informazioni su un livello in una mappa e compilando alcuni annunci sulla classe chiamata strato (sorprendentemente!):LINQ e JSON.NET quando i nomi delle proprietà variano

 using (var client = new WebClient()) 
     { 
      _content = client.DownloadString(_url.AbsoluteUri + OutputFormats.Json); 
     } 

     JObject json = JObject.Parse(_content); 
     IEnumerable<Field> fields = from f in json["fields"].Children() 
            select new Field(
             (string)f["name"], 
             (string)f["alias"], 
             (EsriFieldType)Enum.Parse(typeof(EsriFieldType), (string)f["type"]) 
             ); 
     _fields = fields.ToList(); 
     _displayFieldName = (string)json["displayField"]; 

Si può guardare a questo indirizzo per i dettagli del JSON per questo metodo: http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/WaterTemplate/WaterDistributionNetwork/MapServer/1?f=json&pretty=true. Ma il problema arriva quando ho bisogno di trasformare i singoli campi dati associati ai livelli della mappa in un DataTable o anche solo in una struttura di dizionari. Il problema è che, a differenza dei feed RSS o altri formati coerenti, i nomi dei campi e il numero di campi cambia da livello mappa a livello mappa. Ecco un esempio di me in esecuzione di una query:

[Test] 
    [Category(Online)] 
    public void Can_query_a_single_feature_by_id() 
    { 
     var layer = _map.LayersWithName(ObjectMother.LayerWithoutOID)[0]; 
     layer.FindFeatureById("13141"); 
     Assert.IsNotNull(layer.QueryResults); 
    } 

Il codice che viene eseguito in layer.FindFeatureById è questo e comprende la parte in cui vengo bloccato:

 public void FindFeatureById(string id) 
    { 
     var queryThis = ObjectIdField() ?? DisplayField(); 
     var queryUrl = string.Format("/query{0}&outFields=*&where=", OutputFormats.Json); 
     var whereClause = queryThis.DataType == typeof(string) 
           ? string.Format("{0}='{1}'", queryThis.Name, id) 
           : string.Format("{0}={1}", queryThis.Name, id); 
     var where = queryUrl + HttpUtility.UrlEncode(whereClause); 
     var url = new Uri(_url.AbsoluteUri + where); 
     Debug.WriteLine(url.AbsoluteUri); 
     string result; 

     using (var client = new WebClient()) 
     { 
      result = client.DownloadString(url); 
     } 

     JObject json = JObject.Parse(result); 
     IEnumerable<string> fields = from r in json["fieldAliases"].Children() 
            select ((JProperty)r).Name; 
     // Erm...not sure how to get this done. 
     // Basically need to populate class instances/rows with the 
     // values for each field where the list of fields is not 
     // known beforehand. 

    } 

Potete vedere il JSON sputare dal visitando questo URL (notare la codifica quando si taglia): href = "http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/WaterTemplate/WaterDistributionNetwork/MapServer/1/query?f=json & outFields = * & where = FACILITYID% ​​3d'13141 '

Quindi la mia domanda (finalmente!) È questa. Come faccio a scorrere la collezione di "attributi" nelle "caratteristiche" per ottenere i valori di campo effettivi. Puoi vedere che ho capito come ottenere i nomi dei campi dal campo Alias, ma dopo sono stato perplesso. Sono stato armeggiare con il JsonReader su un file che assomiglia a questo, ma ancora nessuna gioia:

{ 
    "displayFieldName" : "FACILITYID", 
    "fieldAliases" : { 
    "FACILITYID" : "Facility Identifier", 
    "ACCOUNTID" : "Account Identifier", 
    "LOCATIONID" : "Location Identifier", 
    "CRITICAL" : "Critical Customer", 
    "ENABLED" : "Enabled", 
    "ACTIVEFLAG" : "Active Flag", 
    "OWNEDBY" : "Owned By", 
    "MAINTBY" : "Managed By" 
}, 
"features" : [ 
    { 
    "attributes" : { 
     "FACILITYID" : "3689", 
     "ACCOUNTID" : "12425", 
     "LOCATIONID" : "12425", 
     "CRITICAL" : 1, 
     "ENABLED" : 1, 
     "ACTIVEFLAG" : 1, 
     "OWNEDBY" : 1, 
     "MAINTBY" : 1 
    } 
    }, 
    { 
    "attributes" : { 
     "FACILITYID" : "4222", 
     "ACCOUNTID" : "12958", 
     "LOCATIONID" : "12958", 
     "CRITICAL" : 1, 
     "ENABLED" : 1, 
     "ACTIVEFLAG" : 1, 
     "OWNEDBY" : 1, 
     "MAINTBY" : 1 
    } 
    } 
] 
} 

risposta

3

Ebbene si è scoperto il il miglior approccio è stato quello di utilizzare un JsonTextReader e appena strappare attraverso i dati, piuttosto che cercare usare LINQ. Ha un sacco di indentazione che mi rende infelice, ma suppongo che sia un effetto diretto dell'utilizzo di una struttura gerarchica dei dati, in primo luogo. Ecco come stampare l'elenco dei file ("attributi") e le loro collezioni nome/valore:

 using (var file = File.OpenText(_fileWithGeom)) 
     { 
      JsonReader reader = new JsonTextReader(file); 

      while (reader.Read()) 
      { 
       while (Convert.ToString(reader.Value) != "features") 
       { 
        reader.Read(); 
       } 

       Console.WriteLine("Found feature collections"); 

       // ignore stuff until we get to attribute array 

       while (reader.Read()) 
       { 
        switch (Convert.ToString(reader.Value)) 
        { 
         case "attributes": 
          Console.WriteLine("Found feature"); 
          reader.Read(); // get pass attributes property 

          do 
          { 
           // As long as we're still in the attribute list... 
           if (reader.TokenType == JsonToken.PropertyName) 
           { 
            var fieldName = Convert.ToString(reader.Value); 
            reader.Read(); 
            Console.WriteLine("Name: {0} Value: {1}", fieldName, reader.Value); 
           } 

           reader.Read(); 

          } while (reader.TokenType != JsonToken.EndObject 
            && Convert.ToString(reader.Value) != "attributes"); 
          break; 

         case "geometry": 
          Console.WriteLine("Found geometry"); 
          reader.Read(); 
          break; 
        } 
       } 
      } 
     } 

E questa volta sto anche dover gestire la geometria, in modo da controllare questo URL per il JSON che quanto sopra codice è l'analisi:

http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/WaterTemplate/WaterDistributionNetwork/MapServer/7/query?where=OBJECTID%3C10&returnGeometry=true&outSR=&outFields= * & f = pjson

6

per un modo rapido e sporco (non LINQ) per ottenere gli attributi e valori, provare:

JObject jo = JObject.Parse(json); 

foreach (JObject j in jo["features"]) 
{ 
    foreach (JProperty k in j["attributes"]) 
    { 
    Console.WriteLine(k.Name + " = " + k.Value); 
    } 
} 

Non è l'ideale, ma funziona quando non si conoscono i nomi dei campi che verranno restituiti. Se trovo un modo migliore per farlo, lo aggiornerò.

+0

bene questo è bello e semplice. grazie per il suggerimento – Dylan

Problemi correlati