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]);
        }
    }
}
4
  • Why not deserialize to a custom class and then sort it (normally) Commented Nov 15, 2018 at 20:05
  • @Disaffected1070452 that's my last resort, I was trying to avoid creating any custom class. Commented Nov 15, 2018 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 is for recursively sorting a tree of objects by property name so it doesn't really apply here. Commented Nov 15, 2018 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. Commented Nov 15, 2018 at 21:19

1 Answer 1

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.

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.