【问题标题】:Sorting JObject inside array by a field value按字段值对数组内的 JObject 进行排序
【发布时间】:2018-11-16 08:44:49
【问题描述】:

我有一个如下的 JSON 字符串:

{
  "MetaData": {
    "ResourcesUsed": 1
  },
  "Result": [
    {
      "locations": [
        {
          "country": "Papua New Guinea",
          "city": "Jacquinot Bay",
          "locTypeAttributes": {
            "localDate": "2018-10-08T04:21:00-07:00",
            "utcDate": "2018-10-08T04:21:00-07:00",
          },
          "point": {
            "coordinates": [
              151.52,
              -5.6
            ],
            "type": "Point"
          }
        },{
          "country": "Papua New Guinea2",
          "city": "Jacquinot Bay2",
          "locTypeAttributes": {
            "localDate": "2018-10-08T04:21:00-07:00",
            "utcDate": "2018-10-02T04:21:00-07:00",
          },
          "point": {
            "coordinates": [
              151.52,
              -5.6
            ],
            "type": "Point"
          }
        }
      ]
    }
  ]
}

我使用 Newtonsoft 将其转换为 JSON 对象。我想要做的是通过嵌套在每个locations 项目中的utcDate 字段对Result 数组中的locations 数组进行排序。我找到了以下线程:C# Sort JSON string keys。但是,我仍然无法实现它,因为我的对象中有数组,而该问题纯粹涉及按属性名称按字母顺序对对象中的对象进行排序。

这是我目前写的一段代码:

public string GenerateJson()
{
     var model = (JObject)JsonConvert.DeserializeObject(data);
     Sort(model);
}
private void Sort(JObject jObj)
{
    var props = jObj["Result"][0]["locations"].ToList();
    foreach (var prop in props)
    {
        prop.Remove();
    }

    foreach (var prop in props.OrderBy(p => p.Name))
    {
        jObj.Add(prop);
        if (prop.Value is JObject)
            Sort((JObject)prop.Value);
        if (prop.Value is JArray)
        {
            Int32 iCount = prop.Value.Count();
            for (Int32 iIterator = 0; iIterator < iCount; iIterator++)
                if (prop.Value[iIterator] is JObject)
                    Sort((JObject)prop.Value[iIterator]);
        }
    }
}

【问题讨论】:

  • 为什么不反序列化为自定义类,然后对其进行排序(通常)
  • @Disaffected1070452 这是我最后的手段,我试图避免创建任何自定义类。
  • 您要对"Result": [] 数组进行排序,还是要对每个"Result[*].locations" 数组进行排序?因为位置本身是一个数组,所以原则上可能不止一个,每个都有自己的utcDate。如果你确实想对"Results" 数组进行排序,你想如何处理结果有多个位置和多个utcDate 值的情况? C# Sort JSON string keys 用于按属性名称递归地对对象树进行排序,因此它实际上并不适用于此。
  • @dbc 我只是简化了 JSON 字符串,所以在结果中,我们将始终有一个位置数组,我想按 utcDate 对该数组进行排序。

标签: c# json sorting json.net


【解决方案1】:

您可以使用 LINQ 对每个单独的 "Result[*].locations" 数组进行排序,如下所示:

// Load the JSON without parsing or converting any dates.
var model = JsonConvert.DeserializeObject<JObject>(data, new JsonSerializerSettings{ DateParseHandling = DateParseHandling.None });

// Construct a serializer that converts all DateTime values to UTC
var serializer = JsonSerializer.CreateDefault(new JsonSerializerSettings{ DateTimeZoneHandling = DateTimeZoneHandling.Utc });

foreach (var locations in model.SelectTokens("Result[*].locations").OfType<JArray>())
{
    // Then sort the locations by utcDate converting the value to UTC at this time.
    var query = from location in locations
                let utcDate = location.SelectToken("locTypeAttributes.utcDate").ToObject<DateTime>(serializer)
                orderby utcDate
                select location;
    locations.ReplaceAll(query.ToList());
}

注意事项:

  • 最初使用 DateParseHandling.None 加载 JSON,以防止 "localDate""utcDate" 字符串被过早地解释为具有统一 DateTime.KindDateTime 对象。

    (有关 Json.NET 如何解释看起来像日期的字符串的讨论,请参阅 Serializing Dates in JSON。)

  • 然后我们使用SelectTokens("Result[*].locations") 遍历所有"locations" 数组,其中[*]JSONPath 通配符,选择"Results" 数组中的所有条目。

  • 然后,我们通过将嵌套的 locTypeAttributes.utcDate 反序列化为 UTC 日期来对每个 "locations" 数组进行排序,然后使用 LINQ 进行排序。

  • 最后使用JArray.ReplaceAll()更新数组。

  • 如果缺少任何locTypeAttributes.utcDate 属性,将引发异常。如果可能,您可以改为反序列化为 DateTime?

工作示例 .Net fiddle here.

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-03-11
    • 1970-01-01
    • 2014-10-01
    • 1970-01-01
    • 2011-05-16
    相关资源
    最近更新 更多