属性名称不同时的 LINQ 和 JSON.NET
我正在尝试将一些 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。但当我需要将与地图图层关联的各个数据字段转换为数据表甚至字典结构时,问题就出现了。问题在于,与 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="http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/WaterTemplate/WaterDistributionNetwork/MapServer/1/query?f=json&outFields=*&where=FACILITYID%3d'13141'
所以我的问题(最后!)是这样的。我如何循环遍历“features”中的“attributes”集合来获取实际的字段值。您可以看到我已经弄清楚如何从 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
}
}
]
}
I am attempting to parse some JSON content in to C#. For the simpler cases I am having great success with JSON.NET and really appreciate the clean approach offered by the LINQ provider. Here's an example where I'm downloading information about a layer in a map and filling in some properties on a class called (suprisingly!) Layer:
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"];
You can look at this url for details of the JSON for that method: http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/WaterTemplate/WaterDistributionNetwork/MapServer/1?f=json&pretty=true. But the issue comes when I need to turn the individual data fields associate with map layers in to a DataTable or even just a dictionary structure. The problem is that, unlike RSS feeds or other consistent formats, the field names and number of fields changes from map layer to map layer. Here's an example of me running a 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);
}
The code that's run in layer.FindFeatureById is this and includes the part where I get stuck:
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.
}
You can see the JSON spit out by visiting this URL (note the encoding when you cut'n'paste): href="http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/WaterTemplate/WaterDistributionNetwork/MapServer/1/query?f=json&outFields=*&where=FACILITYID%3d'13141'
So my question (at last!) is this. How do I cycle through the collection of "attributes" in the "features" to get the actual field values. You can see that I've figured out how to get the field names from the fieldAliases, but after that I'm stumped. I've been tinkering with the JsonReader on a file that looks like this, but still no joy:
{
"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
}
}
]
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
要以快速而肮脏的(非 LINQ)方式获取属性和值,请尝试以下操作:
这并不理想,但当您不知道将返回的字段名称时,它会起作用。如果我找到更好的方法来做到这一点,我会更新它。
For a quick and dirty (non-LINQ) way to get at the attributes and values, try the following:
It's not ideal, but it works when you don't know the field names that will be coming back. If I find a better way to do this, I'll update it.
事实证明,最好的方法是使用 JsonTextReader 并直接读取数据,而不是尝试使用 LINQ。它有很多缩进,这让我不高兴,但我认为这是首先使用分层数据结构的直接影响。以下是如何打印行列表(“属性”)及其名称/值集合:
这次我还必须处理几何图形,因此请查看此 URL 以获取上述代码正在解析的 JSON:
http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/WaterTemplate/WaterDistributionNetwork/MapServer/7/query?where=OBJECTID%3C10&returnGeometry=true&outSR=&outFields=*& ;f=pjson
Well it turned out the the best approach was to use a JsonTextReader and just rip through the data rather than trying to use LINQ. It's got lots on indentation which makes me unhappy but I suppose that's a direct effect of using a hierarchical data structure in the first place. Here's how to print the list of rows ("attributes") and their name/value collections:
And this time I'm also having to handle geometry, so check out this URL for the JSON that the above code is parsing:
http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/WaterTemplate/WaterDistributionNetwork/MapServer/7/query?where=OBJECTID%3C10&returnGeometry=true&outSR=&outFields=*&f=pjson