Sorting JObject inside array by a field value - c#

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]);
}
}
}

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.

Related

How to convert Json array Keys to lower case?

I am getting a Json Array here is the JSON array
[
{
"customFieldName": "resolution",
"fieldName": "Resolution"
},
{
"customFieldName": "lastViewed",
"fieldName": "Last Viewed"
},
]
I want to convert the Keys to lower case please suggest the solution.
I tried to convert the JSON array to dictionary but not succeeded.
You can try reading all properties inside JsonArray and change the propertyname to lowercase
private static void ConvertToLowerCase(JArray jArray)
{
foreach (var item in jArray.Children())
{
foreach (var property in item.Children<JProperty>().ToList())
{
property.Replace(new JProperty(property.Name.ToLower(), property.Value));
}
}
}
call this method by passing the JArray
var jArray = JArray.Parse(jsonString);
ConvertToLowerCase(jArray);
Console.WriteLine(JsonConvert.SerializeObject(jArray));
Output
[{"customfieldname":"resolution","fieldname":"Resolution"},{"customfieldname":"lastViewed","fieldname":"Last Viewed"}]

C# Json to List

I have a json object as follows:
"dnsNames": {
"type": "array",
"defaultValue": [
"something.else.com",
"something.com",
"else.com"
]
}
I'd like to read that into a List<string> the same way I can read it into a string (i.e. without creating a class for it):
JObject jsonParameters = JObject.Parse(File.ReadAllText(filePath));
string test = jsonParameters["parameters"]["dnsNames"]["defaultValue"].ToString();
Just unsure if that's possible or what the syntax for it might be.
Navigate the object structure as you see it dnsNames.defaultValue then convert that object to a given type (List<string> in our case):
var json =
#"{""dnsNames"": {
""type"": ""array"",
""defaultValue"": [
""something.else.com"",
""something.com"",
""else.com""
]
}}";
var jObject = JObject.Parse(json);
var list = jObject["dnsNames"]["defaultValue"].ToObject<List<string>>();
// ?list
// Count = 3
// [0]: "something.else.com"
// [1]: "something.com"
// [2]: "else.com"

JSON JObject.Parse modifies json string

I have got incomming Json in format:
{
"Type": "value",
"Name": "MeteoStation",
"UniqueAdress": "2C:3A:E8:0F:10:76",
"valuesList": [{
"Value": 23.00,
"Unit": "C",
"Type": "temperature",
"SourceUniqAdress": "2C:3A:E8:0F:10:76",
"TimeCaptured": "2018-03-26T09:36:13.200Z"
}]
}
In my program, I want to create object IValuePacket that is one value in list of values.
JObject jobject = JObject.Parse(incomingJson);
var settings = new JsonSerializerSettings {
NullValueHandling = NullValueHandling.Ignore,
MissingMemberHandling = MissingMemberHandling.Ignore
};
var incommingMessage = JsonConvert.DeserializeObject<MessageEncapsulation>(incomingJson);
string Type = incommingMessage.Type;
string name = incommingMessage.Name;
if (string.IsNullOrWhiteSpace(name))
name = "no name";
if (Type.ToLower().Equals("value")) {
var values = JsonConvert.DeserializeObject<List<IValuePacket>>(jobject["valuesList"].ToString());
}
Everything works fine untill I recieved exact this json as mention above.
JObject.Parse modifies value TimeCaptured and jobject looks like:
{
"Type": "value",
"Name": "Meteostation",
"UniqueAdress": "2C:3A:E8:0F:10:76",
"valuesList": [{
"Value": 23.00,
"Unit": "C",
"Type": "temperature",
"SourceUniqAdress": "2C:3A:E8:0F:10:76",
"TimeCaptured": "2018-03-26T09:36:13.2Z"
}]}
Thats not so much difference, but DateTime.ParseExact(value, "yyyy-MM-ddThh:mm:ss.fffZ", System.Globalization.CultureInfo.InvariantCulture); cannot parse new value. Actually, I am sending 201 ms instead of 200 ms. It works, but I want to have good time for future reasons.
Is there any way how to avoid changing in Json during parsing?
It does not really modify your string, it just parses your date string into DateTime object when you call JObject.Parse. If you do this:
var obj = JObject.Parse(json);
var values = (JArray) obj["valuesList"];
var time = (JValue) values[0]["TimeCaptured"];
Console.WriteLine(time.Value.GetType());
You notice that time.Value is of type DateTime. Then you do this:
JsonConvert.DeserializeObject<List<IValuePacket>>(jobject["valuesList"].ToString());
By doing that you convert valueList back to json, but now TimeCaptured is DateTime and not a string, so that DateTime object is converted to json string using whatever date time format is used by JSON.NET by default.
You can avoid parsing strings that look like dates to .NET DateTime objects by parsing json to JObject like this:
JObject obj;
using (var reader = new JsonTextReader(new StringReader(json))) {
// DateParseHandling.None is what you need
reader.DateParseHandling = DateParseHandling.None;
obj = JObject.Load(reader);
}
Then type of TimeCaptured will be string, as you expect.
Side note: there is no need to convert JToken back to string and then call JsonConvert.Deserialize on that string. Instead, do this:
var values = obj["valuesList"].ToObject<List<IValuePacket>>();

Json.NET SelectTokens not working if json contains empty array

I have a valid JSON object that contains multiple "en-US" keys, which I'm trying select. For that purpose I use the JsonPath
"$..en-US"
which is given to the SelectTokens procedure implemented by the Json.NET. It's a JSON framework for .NET . Everything is working fine and well as long as my JSON doesn't contain any empty array.
Here's an example:
var myJsonPath = "$..en-US";
var myJson =
#"{
'controls': [
{
'messages': {
'addSuggestion': {
'en-US': 'Add'
}
}
},
{
'header': {
'controls': []
},
'controls': [
{
'controls': [
{
'defaultCaption': {
'en-US': 'Sort by'
},
'sortOptions': [
{
'label': {
'en-US': 'Name'
}
}
]
}
]
}
]
}
]
}";
var jToken = JObject.Parse(myJson);
var tokens = jToken.SelectTokens(myJsonPath);
Here, the tokens variable will contain just one element! That will be the "en-US" occurence BEFORE the empty array in the 'controls' of the 'header' object. However, when I just leave this 'header' object out:
var myJson =
#"{
'controls': [
{
'messages': {
'addSuggestion': {
'en-US': 'Add'
}
}
},
{
'controls': [
{
'controls': [
{
'defaultCaption': {
'en-US': 'Sort by'
},
'sortOptions': [
{
'label': {
'en-US': 'Name'
}
}
]
}
]
}
]
}
]
}";
I will get all the 3 occurencies of the "en-US" as expected. Btw, if I validate my JsonPath on the first JSON object (i.e. which contains an empty array) in an online tool, then as expected, I get all the three "en-US" cases. This diverges from what I'm getting from the Json.NET. I'm wondering whether it's a bug or do I have to handle this case manually somehow?
This is a bug that has been fixed. Upgrade to the latest version of Json.NET.
If you're in the same situation as me where you're a bit stuck with respect to updating your version of Json.NET, you can work around the issue by doing something along the lines of this:
IEnumerable<JValue> vals = jToken
.Desecendants()
.Where(w => w is JProperty && w.Name=="en-US")
.Select(s => s.Value);
Hope that helps! The vals array will contain the same tokens you would have gotten using the selector you were trying to use before.

Convert specific attribute value in a JSON object to IEnumerable in C#

I have a JSON object that looks like this
{
"totalCount": 2,
"students": [{
"name": "abc",
"data": {
"Maths": 20,
"Science": 25
},
"score": 10.0
},
{
"name": "xyz",
"data": {
"Maths": 44,
"Science": 12
},
"score": 11.0
}]
}
I want to deserialize this JSON object to an IEnumerable<String> that contains all the names.
I want -
private IEnumerable<String> GetAllNames(string json) to return ["abc","xyz"]
This is just sample data (and not homework!!). Any advice on how to achieve this would be appreciated. I'm using Newtonsoft library but haven't been able to do this effectively yet. I do not want to iterate through the objects and construct the list myself, any direct way of doing this?
EDIT -
This is what I'm doing currently
var studentList = new List<string>();
var json = JsonConvert.DeserializeObject<dynamic>(jsonString);
foreach (var data in json.students)
{
catalogsList.Add(data.name.toString());
}
return catalogsList;
Try this:
private IEnumerable<string> GetAllNames(string json)
{
JObject jo = JObject.Parse(json);
return jo["students"].Select(s => s["name"].ToString());
}

Categories

Resources