0

I have a JSON string like below:

{
  "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"
          }
        }
      ]
    }
  ]
}

I converted it to a JSON object using Newtonsoft. What I want to do is to sort the locations array(s) inside the Result array by the utcDate field nested in each locations item. I found the following thread: C# Sort JSON string keys. However, I could not still implement it since I have arrays inside my object, while that question deals purely with sorting objects inside objects alphabetically by property name.

Here is a piece of code that I wrote so far:

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]);
        }
    }
}
dbc
  • 104,963
  • 20
  • 228
  • 340
DeadlyDagger
  • 109
  • 1
  • 12
  • Why not deserialize to a custom class and then sort it (normally) – Ňɏssa Pøngjǣrdenlarp Nov 15 '18 at 20:05
  • @Disaffected1070452 that's my last resort, I was trying to avoid creating any custom class. – DeadlyDagger Nov 15 '18 at 20:08
  • Do you want to sort the `"Result": []` array, or do you want to sort each `"Result[*].locations"` array? Because the locations themselves are an array so there might in principle be more than one, each with its own `utcDate`. And if you do want to sort the `"Results"` array how do you want to deal with the situation where a result has multiple locations with multiple `utcDate` values? [C# Sort JSON string keys](https://stackoverflow.com/q/14417235/3744182) is for recursively sorting a tree of objects by property name so it doesn't really apply here. – dbc Nov 15 '18 at 20:38
  • @dbc I just simplified the JSON string, so in Result, we will always have one array of locations, I want to sort that array by `utcDate`. – DeadlyDagger Nov 15 '18 at 21:19

1 Answers1

0

You can sort each individual "Result[*].locations" array using LINQ as follows:

// 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());
}

Notes:

  • The JSON is initially loaded using DateParseHandling.None to prevent the "localDate" and "utcDate" strings from being prematurely interpreted as DateTime objects with a uniform DateTime.Kind.

    (For a discussion of how Json.NET interprets strings that look like dates, see Serializing Dates in JSON.)

  • We then iterate through all "locations" arrays using SelectTokens("Result[*].locations") where [*] is the JSONPath wildcard character, selecting all entries in the "Results" array.

  • We then order each "locations" array by deserializing the nested locTypeAttributes.utcDate to a UTC date, then ordering using LINQ.

  • Finally the array is updated using JArray.ReplaceAll().

  • If any locTypeAttributes.utcDate property is missing, an exception will be thrown. You could instead deserialize to DateTime? if that is a possibility.

Working sample .Net fiddle here.

dbc
  • 104,963
  • 20
  • 228
  • 340