Вопрос

Я пытаюсь разобрать некоторое содержимое JSON в C #.В более простых случаях я добиваюсь большого успеха JSON.NET и действительно ценю чистый подход, предлагаемый поставщиком LINQ.Вот пример, в котором я загружаю информацию о слое на карте и заполняю некоторые свойства в классе с именем (удивительно!) Слой:

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

Вы можете посмотреть на этот URL-адрес для получения подробной информации о JSON для этого метода: http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/WaterTemplate/WaterDistributionNetwork/MapServer/1?f=json&pretty=true.Но проблема возникает, когда мне нужно преобразовать отдельные поля данных, связанные со слоями карты, в DataTable или даже просто словарную структуру.Проблема в том, что, в отличие от RSS-каналов или других согласованных форматов, названия полей и количество полей меняются от слоя карты к слою.Вот пример того, как я выполняю запрос:

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

Код, который выполняется в layer.FindFeatureById - это то, что включает в себя ту часть, где я застреваю:

        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.

    }

Вы можете просмотреть JSON-файл, перейдя по этому URL (обратите внимание на кодировку при вырезании и вставке).:href="http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/WaterTemplate/WaterDistributionNetwork/MapServer/1/query?f=json&outFields=*&where=FACILITYID%3d'13141'

Итак, мой вопрос (наконец-то!) заключается в следующем.Как мне перебрать коллекцию "атрибутов" в разделе "функции", чтобы получить фактические значения полей?Вы можете видеть, что я выяснил, как получить имена полей из fieldAliases, но после этого я в тупике.Я повозился с JsonReader над файлом, который выглядит примерно так, но по-прежнему без радости:

{
  "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
    }
  }
]
}
Это было полезно?

Решение 2

Ну, оказалось, что лучшим подходом было использовать JsonTextReader и просто просматривать данные, а не пытаться использовать LINQ.В нем много отступов, что делает меня несчастным, но я полагаю, что это прямой эффект использования иерархической структуры данных в первую очередь.Вот как распечатать список строк ("атрибуты") и их коллекции имен / значений:

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

И на этот раз мне также приходится обрабатывать геометрию, поэтому проверьте этот URL на наличие JSON, который анализирует приведенный выше код:

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

Другие советы

Для быстрого и грязного (без LINQ) способа получить доступ к атрибутам и значениям попробуйте следующее:

JObject jo = JObject.Parse(json);

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

Это не идеально, но это работает, когда вы не знаете названий полей, которые будут возвращены.Если я найду лучший способ сделать это, я обновлю его.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top