2009-09-10 22 views
9

Estoy intentando analizar el contenido JSON en C#. Para los casos más simples, estoy teniendo un gran éxito con JSON.NET y realmente aprecio el enfoque limpio que ofrece el proveedor de LINQ. He aquí un ejemplo en el que estoy descargando información sobre una capa en un mapa y rellenar algunas propiedades de una clase llamada capa (sorprendentemente!):LINQ y JSON.NET cuando los nombres de las propiedades varían

 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"]; 

Usted puede mirar en esta dirección URL para obtener información sobre el JSON para que método: http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/WaterTemplate/WaterDistributionNetwork/MapServer/1?f=json&pretty=true. Pero el problema surge cuando necesito convertir los campos de datos individuales asociados con capas de mapas en una DataTable o incluso solo en una estructura de diccionario. El problema es que, a diferencia de los canales RSS u otros formatos consistentes, los nombres de los campos y el número de campos cambian de capa de mapa a capa de mapa. He aquí un ejemplo de mi ejecución de una consulta:

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

El código que se ejecuta en layer.FindFeatureById es este e incluye la parte en que se queda bloqueado:

 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. 

    } 

Se puede ver el JSON escupir a cabo por visitando esta URL (fíjate en la codificación cuando cortes): href = "http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/WaterTemplate/WaterDistributionNetwork/MapServer/1/query?f=json & outFields = * & donde = FACILITYID% ​​3d'13141 '

Entonces mi pregunta (¡por fin!) Es esto. ¿Cómo ciclo a través de la colección de "atributos" en las "características" para obtener los valores de campo reales. Puedes ver que he descubierto cómo obtener los nombres de campo de los fieldAliases, pero después de eso estoy perplejo. He estado jugando con la JsonReader en un archivo que tiene este aspecto, pero todavía no hay alegría:

{ 
    "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 
    } 
    } 
] 
} 

Respuesta

3

Pues resultó que el el mejor enfoque era utilizar un JsonTextReader y simplemente extraer a través de los datos en lugar de tratar para usar LINQ. Tiene muchas sangrías que me hacen infeliz, pero supongo que es un efecto directo del uso de una estructura de datos jerárquica en primer lugar. Aquí se explica cómo imprimir la lista de filas ("atributos") y sus colecciones de nombre/valor:

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

Y esta vez también estoy tener que manejar la geometría, así que echa un vistazo a este URL para el JSON que el anterior código está analizando:

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

6

para una manera rápida y sucia (no LINQ) para llegar a los atributos y valores, intente lo siguiente:

JObject jo = JObject.Parse(json); 

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

No es ideal, pero funciona cuando no se conocen los nombres de los campos que volverán. Si encuentro una mejor manera de hacerlo, lo actualizaré.

+0

bueno eso es bueno y simple. Gracias por la sugerencia. – Dylan

Cuestiones relacionadas