プロパティ名が異なる場合のLINQおよびJSON.NET
-
05-07-2019 - |
質問
一部の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"];
このメソッドのJSONの詳細については、次のURLをご覧ください: 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.
}
このURLにアクセスすると、JSONの吐き出しを見ることができます(カットアンドペーストするときのエンコーディングに注意してください):href =&quot; http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/WaterTemplate/WaterDistributionNetwork / MapServer / 1 / query?f = json&amp; outFields = *&amp; 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
最良のアプローチは、LINQを使用するのではなく、JsonTextReaderを使用してデータをリッピングすることであることが判明しました。インデントがたくさんあるので不満に思うが、そもそも階層データ構造を使用することの直接的な影響だと思う。行のリスト(&quot; attributes&quot;)とその名前/値のコレクションを印刷する方法は次のとおりです。
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;
}
}
}
}
そして今回はジオメトリも処理する必要があるので、上記のコードが解析しているJSONのURLを確認してください:
他のヒント
属性および値を取得するための迅速でダーティーな(非LINQ)方法については、次を試してください:
JObject jo = JObject.Parse(json);
foreach (JObject j in jo["features"])
{
foreach (JProperty k in j["attributes"])
{
Console.WriteLine(k.Name + " = " + k.Value);
}
}
これは理想的ではありませんが、戻ってくるフィールド名がわからないときに機能します。これを行うためのより良い方法が見つかったら、更新します。