Question

J'essaie d'analyser du contenu JSON en C #. Pour les cas les plus simples, j’ai un grand succès avec JSON.NET et j’apprécie vraiment l’approche claire proposée par le fournisseur LINQ. Voici un exemple où je télécharge des informations sur une couche dans une carte et que je renseigne certaines propriétés sur une classe appelée (étonnamment!) Couche:

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

Vous pouvez consulter cette URL pour plus de détails sur le JSON de cette méthode: http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/WaterTemplate/WaterDistributionNetwork/MapServer/1?f=json&pretty=true . Mais le problème survient lorsque je dois transformer les champs de données individuels associés aux couches de carte en un DataTable ou même simplement une structure de dictionnaire. Le problème est que, contrairement aux flux RSS ou à d'autres formats cohérents, les noms et le nombre de champs changent d'une couche à l'autre. Voici un exemple d’exécution d’une requête:

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

Le code qui est exécuté dans layer.FindFeatureById est le suivant et comprend la partie où je suis bloqué:

        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.

    }

Vous pouvez voir le fichier JSON en visitant cette URL (notez le codage lorsque vous coupez et collez): href =

Donc ma question (enfin!) est la suivante. Comment parcourir la collection d’attributs " dans les "fonctionnalités" pour obtenir les valeurs réelles du champ. Vous pouvez voir que j'ai compris comment obtenir les noms de champs à partir de fieldAliases, mais après cela, je suis perplexe. J'ai bricolé le JsonReader sur un fichier qui ressemble à ceci, mais toujours pas de joie:

{
  "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
    }
  }
]
}
Était-ce utile?

La solution 2

Il s’est avéré que la meilleure approche consistait à utiliser un JsonTextReader et à simplement extraire les données au lieu d’essayer d’utiliser LINQ. Il y a beaucoup d'indentation, ce qui me rend malheureux, mais je suppose que cela résulte directement de l'utilisation d'une structure de données hiérarchique. Voici comment imprimer la liste des lignes ("attributs") et leurs collections de noms / valeurs:

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

Et cette fois, je dois également gérer la géométrie. Consultez donc cette URL pour le JSON que le code ci-dessus est en train d'analyser:

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

Autres conseils

Pour obtenir un moyen rapide et transparent (non-LINQ) d'accéder aux attributs et aux valeurs, procédez comme suit:

JObject jo = JObject.Parse(json);

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

Ce n’est pas idéal, mais cela fonctionne lorsque vous ne connaissez pas les noms de champs qui reviendront. Si je trouve un meilleur moyen de le faire, je le mettrai à jour.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top