Note: Im working with System.Text.Json package
Below is JSON I am getting from a database. I have to go through each of the keys in the JSON and check if there is a period (.) in the key name; if so, I need to add a property required with the value of true in the JSON in order to provide runtime validation:
validate:{"required", true}
Here is my JSON:
{
"display": "wizard",
"settings": {},
"components": [{
"title": "Event Information",
"label": "Event Information",
"type": "panel",
"key": "EventInformation",
"components": [{
"label": "Row1Columns",
"columns": [{
"components": [{
"label": "Event Date",
"format": "dd/MM/yyyy hh:mm a",
"tableView": false,
"datePicker": {
"disableWeekends": false,
"disableWeekdays": false
},
"validate": {
"unique": true
},
"key": "Event.EventDate",
"type": "datetime",
"input": true,
"suffix": "<i ref=\"icon\" class=\"fa fa-calendar\" style=\"\"></i>",
"widget": {
"type": "calendar",
"displayInTimezone": "viewer",
"language": "en",
"useLocaleSettings": false,
"allowInput": true,
"mode": "single",
"enableTime": true,
"noCalendar": false,
"format": "dd/MM/yyyy hh:mm a",
"hourIncrement": 1,
"minuteIncrement": 1,
"time_24hr": false,
"minDate": null,
"disableWeekends": false,
"disableWeekdays": false,
"maxDate": null
}
}],
"width": 6,
"offset": 0,
"push": 0,
"pull": 0
}, {
"components": [{
"label": "Duration (minutes)",
"mask": false,
"spellcheck": true,
"tableView": false,
"delimiter": false,
"requireDecimal": false,
"inputFormat": "plain",
"key": "Event.Duration",
"type": "number",
"input": true
}],
"width": 6,
"offset": 0,
"push": 0,
"pull": 0
}],
"tableView": false,
"key": "row1Columns",
"type": "columns",
"input": false
}, {
"label": "Row2Columns",
"columns": [{
"components": [{
"label": "Event Category",
"widget": "choicesjs",
"tableView": true,
"dataSrc": "custom",
"data": {
"custom": "values = getEventCategoryValues()"
},
"valueProperty": "AgencyEventCategoryId",
"template": "<span>{{ item.text }}</span>",
"selectThreshold": 0.3,
"validate": {
"required": true
},
"key": "Event.AgencyEventCategoryId",
"type": "select",
"indexeddb": {
"filter": {}
},
"input": true
}],
"width": 6,
"offset": 0,
"push": 0,
"pull": 0
}, {
"components": [{
"label": "Attendance",
"widget": "choicesjs",
"tableView": true,
"multiple": false,
"dataSrc": "custom",
"data": {
"custom": "values = getAttendanceValues()"
},
"valueProperty": "AgencyEventAttendanceId",
"template": "<span>{{ item.text }}</span>",
"selectThreshold": 0.3,
"validate": {
"required": true,
},
"key": "Event.AgencyEventAttendanceId",
"type": "select",
"indexeddb": {
"filter": {}
},
"input": true
}],
"width": 6,
"offset": 0,
"push": 0,
"pull": 0
}],
"tableView": false,
"key": "row2Columns",
"type": "columns",
"input": false
}, {
"label": "Event Options",
"widget": "choicesjs",
"tableView": true,
"multiple": true,
"dataSrc": "custom",
"data": {
"custom": "values = getEventManagerValues(data.Event.AgencyEventCategoryId)"
},
"template": "<span>{{ item.text }}</span>",
"refreshOn": "Event.AgencyEventCategoryId",
"clearOnRefresh": true,
"selectThreshold": 0.3,
"calculateServer": false,
"validate": {
"required": true,
"multiple": true
},
"key": "Event.EventDetail",
"type": "select",
"indexeddb": {
"filter": {}
},
"input": true
}, {
"label": "Casenote",
"wysiwyg": true,
"autoExpand": true,
"spellcheck": true,
"tableView": true,
"calculateServer": false,
"key": "Event.EventCasenote[0].Casenote",
"type": "textarea",
"input": true
}],
"input": false,
"tableView": false,
"breadcrumbClickable": true,
"buttonSettings": {
"previous": true,
"cancel": true,
"next": true
},
"collapsible": false
}]
}
One of the options would be to parse json into JObject and add property to it via Newtonsoft's Json.NET:
var obj = JObject.Parse("{'key':'value'}");
obj.Add("required", true);
Console.WriteLine(obj); // { "key": "value", "required": true }
To add new object you can use this overload of Add:
obj.Add("validate", JObject.FromObject(new { required = true }));
So the whole solution will look something like this:
var obj = JObect.Parse(your_json);
foreach(var token in obj.DescendantsAndSelf().ToList()) // ToList is important!!!
{
if(token is JObject xObj)
{
// check your conditions for adding property
// check if object does not have "validate" property
if(satisfies_all_conditions)
{
xObj.Add("validate", JObject.FromObject(new { required = true }));
}
}
}
I was having similar issue, and have found a solution, see below the code and comments. I am using Newtonsoft though but it is worth checking if you can use Newtonsoft library and have not found a solution for System.Text.Json.
All controls in your JSON are part of component object/array so we can use JSONPath, see the link for more details.
public void IterateJson(JObject obj, string mandatoryFieldKey)
{
JToken jTokenFoundForMandatoryField = obj.SelectToken
("$..components[?(#.key == '" + mandatoryFieldKey + "')]");
//Now we convert this oken into an object so that we can add properties/objects in it
if (jTokenFoundForMandatoryField is JObject jObjectForMandatoryField)
{
//We check if validate already exists for this field, if it does not
//exists then we add validate and required property inside the if condition
if (jObjectForMandatoryField["validate"] == null)
jObjectForMandatoryField.Add("validate",
JObject.FromObject(new { required = true })); //add validate and required property
else
{
//If validate does not exists then code comes here and
//we convert the validate into a JObject using is JObject statement
if (jObjectForMandatoryField["validate"] is JObject validateObject)
{
//We need to check if required property already exists,
//if it does not exists then we add it inside the if condition.
if (validateObject["required"] == null)
{
validateObject.Add("required", true); //add required property
}
}
}
}
}
It was interesting task for me, so this is what i have written
class Program
{
static void Main(string[] args)
{
string jsonFilePath = #"test.json"; //path to your json
string json = File.ReadAllText(jsonFilePath);
var data = JObject.Parse(json);
CheckJson(data);
Console.ReadLine();
}
static void CheckJson(JToken value)
{
if (value.Values().Count() != 0) //if more than 0 - so value is object or array and we have to call this method for each property
{
foreach (var item in value.Values().ToList())
{
CheckJson(item);
}
}
else if (true) //else - we have exactly value of key, which we can check, for example if . exists or additional checks
{
if (value.Parent.Parent is JObject jObject && jObject["validate"] == null) //check if above "required" property exists
{
jObject.Add("validate", JObject.FromObject(new { required = true })); //add required property
}
}
}
}
This code will add in each object property "required" if it is not exists.
All you need - just add Validation method and call it in if block.
And better to add additional validation that won't continue checking all properties if "required" property exists
Related
I get the following JSON file by accessing a REST API in C#
[{
"id": 71,
"parent_id": 0,
"name": "Espace priv\u00e9",
"can_read": true,
"can_download": true,
"can_write": true,
"can_edit": true,
"can_delete": true,
"can_share": true,
"creation": "2022-07-12T09:53:34+00:00",
"modification": "2022-07-12T09:53:34+00:00",
"owner": "Sylvain KRIER",
"owner_id": "23"
}, {
"id": 80,
"parent_id": 0,
"name": "CLI12",
"can_read": true,
"can_download": true,
"can_write": true,
"can_edit": true,
"can_delete": true,
"can_share": true,
"creation": "2022-07-13T07:43:25+00:00",
"modification": "2022-07-13T08:25:46+00:00",
"owner": "Patrimoine Click",
"owner_id": "2",
"flags": 2
}, {
"id": 53,
"parent_id": 0,
"name": "DOCUMENTATION",
"can_read": true,
"can_download": true,
"can_write": true,
"can_edit": true,
"can_delete": true,
"can_share": true,
"creation": "2022-06-30T14:38:55+00:00",
"modification": "2022-06-30T14:39:05+00:00",
"owner": "Patrimoine Click",
"owner_id": "2",
"flags": 2
}, {
"id": 77,
"parent_id": 0,
"name": "Mme AMSELLEM Smadar",
"can_read": true,
"can_download": true,
"can_write": true,
"can_edit": true,
"can_delete": true,
"can_share": true,
"creation": "2022-07-12T10:33:29+00:00",
"modification": "2022-07-12T10:33:42+00:00",
"owner": "Patrimoine Click",
"owner_id": "2",
"flags": 2
}, {
"id": 68,
"parent_id": 0,
"name": "Mr NGUIMFACK Guy",
"can_read": true,
"can_download": true,
"can_write": true,
"can_edit": true,
"can_delete": true,
"can_share": true,
"creation": "2022-07-12T08:41:33+00:00",
"modification": "2022-07-12T13:28:06+00:00",
"owner": "Patrimoine Click",
"owner_id": "2",
"flags": 2
}, {
"id": 83,
"parent_id": 0,
"name": "vergne-CLI1",
"can_read": true,
"can_download": true,
"can_write": true,
"can_edit": true,
"can_delete": true,
"can_share": true,
"creation": "2022-07-13T08:20:06+00:00",
"modification": "2022-07-13T08:23:29+00:00",
"owner": "Sylvain KRIER",
"owner_id": "23",
"flags": 2
}, {
"id": 110,
"parent_id": 0,
"name": "krier1",
"can_read": true,
"can_download": true,
"can_write": true,
"can_edit": true,
"can_delete": true,
"can_share": true,
"creation": "2022-07-21T08:57:35+00:00",
"modification": "2022-07-21T08:57:35+00:00",
"owner": "Sylvain KRIER",
"owner_id": "23",
"flags": 2
}
]
It's a folder list. I need to get the "id" of each folder with the "name"
I try to deserialize the JSON string like this, but it doesn't work :
var a = JObject.Parse(((RestSharp.RestResponseBase)resultat.Value).Content).SelectToken("id").ToList();
((RestSharp.RestResponseBase)resultat.Value).Content is the JSON string mentioned
Thanks for giving me help.
Personally, I always deserialize my JSON to a statically typed object before doing anything else with it, in this case you can just create a mostly empty JsonDirectory class with just the id property and deserialize to a list of it, like so
public class JsonDirectory
{
[JsonPropertyName("id")]
public int Id { get; set; }
}
// Deserialize
var json = "...";
var jsonDirectory = JsonSerializer.Deserialize<List<JsonDirectory>>(json);
foreach (var jd in jsonDirectory)
Console.WriteLine(jd.Id);
Here's a demo showcasing his working on your example JSON
P.S this answer uses System.Text.Json, if you're using Newtonsoft.Json then you'll need to change out [JsonPropertyName("id")] with [JsonProperty("id")] and the deserialization line to JsonConvert.DeserializeObject<List<JsonDirectory>>(json)
you can use linq to create a list
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
List<IdName> idNameList = JArray.Parse(json)
.Select(j => new IdName { Id = (int)j["id"], Name = (string)j["name"] }).ToList();
public class IdName
{
[JsonProperty("id")]
public int Id { get; set; }
[JsonProperty("name")]
public int Name { get; set; }
}
result
[
{
"Id": 71,
"Name": "Espace privé"
},
{
"Id": 80,
"Name": "CLI12"
},
{
"Id": 53,
"Name": "DOCUMENTATION"
},
{
"Id": 77,
"Name": "Mme AMSELLEM Smadar"
},
{
"Id": 68,
"Name": "Mr NGUIMFACK Guy"
},
{
"Id": 83,
"Name": "vergne-CLI1"
},
{
"Id": 110,
"Name": "krier1"
}
]
{
"STATUS": "OK",
"projects": [
{
"startDate": "",
"last-changed-on": "2019-01-03T11:46:14Z",
"logo": "",
"created-on": "2018-12-12T10:04:47Z",
"privacyEnabled": false,
"status": "active",
"boardData": {},
"replyByEmailEnabled": true,
"harvest-timers-enabled": false,
"description": "",
"category": {
"color": "",
"id": "",
"name": ""
},
"id": "322852",
"overview-start-page": "default",
"start-page": "projectoverview",
"integrations": {
"xero": {
"basecurrency": "",
"countrycode": "",
"enabled": false,
"connected": "NO",
"organisation": ""
},
"sharepoint": {
"account": "",
"foldername": "root",
"enabled": false,
"folder": "root"
},
"microsoftConnectors": {
"enabled": false
},
"onedrivebusiness": {
"account": "",
"foldername": "root",
"enabled": false,
"folder": "root"
}
},
"defaults": {
"privacy": ""
},
"notifyeveryone": false,
"filesAutoNewVersion": false,
"defaultPrivacy": "open",
"tasks-start-page": "default",
"starred": false,
"announcementHTML": "",
"isProjectAdmin": true,
"name": "Project 2",
"company": {
"is-owner": "1",
"id": "78494",
"name": "MCG Company"
},
"endDate": "",
"announcement": "",
"show-announcement": false,
"subStatus": "current",
"tags": []
},
{
"startDate": "",
"last-changed-on": "2018-12-11T17:52:57Z",
"logo": "",
"created-on": "2018-11-26T11:11:00Z",
"privacyEnabled": false,
"status": "active",
"boardData": {},
"replyByEmailEnabled": true,
"harvest-timers-enabled": false,
"description": "",
"category": {
"color": "",
"id": "",
"name": ""
},
"id": "321041",
"overview-start-page": "default",
"portfolioBoards": [
{
"card": {
"id": "4771"
},
"board": {
"id": "544",
"name": "Project Implementations",
"color": "#F39C12"
},
"column": {
"id": "1573",
"name": "Go Live",
"color": "#F1C40F"
}
}
],
"start-page": "projectoverview",
"integrations": {
"xero": {
"basecurrency": "",
"countrycode": "",
"enabled": false,
"connected": "NO",
"organisation": ""
},
"sharepoint": {
"account": "",
"foldername": "root",
"enabled": false,
"folder": "root"
},
"microsoftConnectors": {
"enabled": false
},
"onedrivebusiness": {
"account": "",
"foldername": "root",
"enabled": false,
"folder": "root"
}
},
"defaults": {
"privacy": ""
},
"notifyeveryone": false,
"filesAutoNewVersion": false,
"defaultPrivacy": "open",
"tasks-start-page": "default",
"starred": false,
"announcementHTML": "",
"isProjectAdmin": true,
"name": "Project One",
"company": {
"is-owner": "1",
"id": "78494",
"name": "MCG Company"
},
"endDate": "",
"announcement": "",
"show-announcement": false,
"subStatus": "current",
"tags": []
}
]
}
This is the JSON response that I'm getting from an app, and there are a lot of other API gets that are returning the same kind of response (nested), so this has to be done dynamically as the user is adding API calls from a config file, so I cannot make pre-made classes with gets and sets.
My goal is to transform this data into a datatable to be inserted into a database
When I see a nested column, my goal is to have the parent column name attached to it with an "_" ex: category_id = ""
or integrations_xero_basecurrency = "", etc..
This is the code that I used to tabulate the data, but in the code it's only taking the column if it's a JValue (key and value), and I'm not able for the life of me to create a proper loop that will do the trick.
public DataTable Tabulate(string jsonContent)
{
var jsonLinq = JObject.Parse(jsonContent);
// Find the first array using Linq
var srcArray = jsonLinq.Descendants().Where(d => d is JArray).First();
//Console.WriteLine("extarcted data:" + srcArray);
var trgArray = new JArray();
foreach (JObject row in srcArray.Children<JObject>())
{
var cleanRow = new JObject();
foreach (JProperty column in row.Properties())
{
// Only include JValue types
if (column.Value is JValue)
{
cleanRow.Add(column.Name, column.Value);
}
}
trgArray.Add(cleanRow);
}
DataTable dt = JsonConvert.DeserializeObject<DataTable>(trgArray.ToString());
return dt;
}
How about something like this:
public DataTable Tabulate(string jsonContent)
{
var jsonLinq = JObject.Parse(jsonContent);
// Find the first array using Linq
var arrayProp = jsonLinq.Properties().First(p => p.Value is JArray);
var srcArray = (JArray)arrayProp.Value;
// Set up a regex consisting of the array property name and subscript
// (e.g. "projects[0]."), which we will strip off
var regex = new Regex($#"^{arrayProp.Name}\[\d+\]\.");
// Flatten each object of the original array
// into new objects and put them in a new array
var trgArray = new JArray(
srcArray.Children<JObject>()
.Select(row => new JObject(
row.Descendants()
.OfType<JProperty>()
.Where(p => p.Value is JValue)
.Select(p => new JProperty(
regex.Replace(p.Value.Path, "").Replace(".", "_"),
p.Value
))
))
);
// Convert the new array to a DataTable
DataTable dt = trgArray.ToObject<DataTable>();
return dt;
}
Working demo: https://dotnetfiddle.net/yrmcSQ
I have a JSON file.
{
"time": [
{
"id": "9999",
"name": "Foo",
"subitem": [
{
"name": "Bar",
"id": "99990",
"visible": true,
"subitem": [
{
"id": "999901",
"name": "Flex",
"visible": true
},
{
"name": "Bear",
"id": "999902",
"visible": true
},
{
"name": "James",
"id": "999903",
"visible": true
}
]
},
{
"name": "Smith",
"id": "999966",
"visible": true
},
{
"name": "John",
"id": "999933",
"visible": true
}
],
"visible": true
},
{
"name": "Doe",
"id": "1111",
"visible": true,
"subitem": [
{
"name": "Jack",
"id": "111111",
"visible": true
},
{
"name": "Wilson",
"id": "111188",
"visible": true
},
{
"name": "Andy",
"id": "111144",
"visible": true
},
{
"name": "Gibbs",
"id": "111155",
"visible": true
}
]
}
],
"name": "asdf",
"id": "13",
"visible": true
}
I also have a JObject and a method to get all the JSON data and store it in this object.
json1 = ti.GetTimeItems();
I have 2 methods in another class to write to the JSON file. Where datafolder is the path.
public void WriteToJson(JObject obj)
{
string fileName = dataFolder + "json1.json";
WriteToJson(fileName, obj);
}
private void WriteToJson(string fileName, JObject obj)
{
using (StreamWriter file = File.CreateText(fileName))
using (JsonTextWriter writer = new JsonTextWriter(file))
{
obj.WriteTo(writer);
}
}//end WriteToJson
Then i have a windows form where i want to take the text from 2 textboxes and add these to the JSON file.
Finally i have my click event
private void button1_Click_1(object sender, EventArgs e)
{
//string json = File.ReadAllText(url);
//JArray time = (JArray)json1.SelectToken("time");
json1.Add(new JObject(
new JProperty("name", textBoxName.Text),
new JProperty("id", textBoxId.Text),
new JProperty("visible", true)));
ti.WriteToJson(json1);
}
My problem is that i cannot seem to write to the array "time" in the JObject.
I managed to write to the file but in root instead of inside the array.
I have tried json1.SelectToken("time") and lots of different approaches, like this one http://stackoverflow.com/questions/15413825/how-do-you-add-a-jtoken-to-an-jobject#15782238 and also some approaches from the Newtonsoft documentation.
Any help is appriciated
Problem solved by ((JArray)json1.GetValue("time")). Selecting the array in the JObject json1 and adding to that instead of the root.
Hope this will help someone.
((JArray)json1.GetValue("time")).Add(
new JObject(
new JProperty("name", textBoxName.Text),
new JProperty("id", textBoxId.Text),
new JProperty("visible", true)));
ti.WriteToJson(json1);
Essentially I have a large (~1 GB) file in which each line is a JSON object, containing nested properties, some of whose values may be arrays of objects.
Example of an object:
{
"business_id": "b9WZJp5L1RZr4F1nxclOoQ",
"full_address": "1073 Washington Ave\nCarnegie, PA 15106",
"hours": {
"Monday": {
"close": "14:30",
"open": "06:00"
},
"Tuesday": {
"close": "14:30",
"open": "06:00"
},
"Friday": {
"close": "14:30",
"open": "06:00"
},
"Wednesday": {
"close": "14:30",
"open": "06:00"
},
"Thursday": {
"close": "14:30",
"open": "06:00"
},
"Sunday": {
"close": "12:30",
"open": "07:00"
},
"Saturday": {
"close": "12:30",
"open": "06:00"
}
},
"open": true,
"categories": ["Breakfast & Brunch", "Restaurants"],
"city": "Carnegie",
"review_count": 38,
"name": "Gab & Eat",
"neighborhoods": [],
"longitude": -80.084799799999999,
"state": "PA",
"stars": 4.5,
"latitude": 40.396744099999999,
"attributes": {
"Alcohol": "none",
"Noise Level": "average",
"Has TV": true,
"Attire": "casual",
"Ambience": {
"romantic": false,
"intimate": false,
"classy": false,
"hipster": false,
"divey": true,
"touristy": false,
"trendy": false,
"upscale": false,
"casual": true
},
"Good for Kids": true,
"Wheelchair Accessible": false,
"Delivery": false,
"Caters": true,
"BYOB": false,
"Corkage": false,
"Accepts Credit Cards": false,
"BYOB/Corkage": "yes_free",
"Take-out": true,
"Price Range": 1,
"Outdoor Seating": false,
"Takes Reservations": false,
"Waiter Service": true,
"Wi-Fi": "no",
"Order at Counter": true,
"Good For": {
"dessert": false,
"latenight": false,
"lunch": false,
"dinner": false,
"brunch": false,
"breakfast": true
},
"Parking": {
"garage": false,
"street": false,
"validated": false,
"lot": true,
"valet": false
},
"Good For Kids": true,
"Good For Groups": false
},
"type": "business"
},
How do I flatten this and convert it into CSV so that I have a single object with properties (columns) like business_id, hours.Monday.close, attributes.Ambience.hipster etc?
The issue is that not all the objects have all the properties, so I need to scan the entire file to get a list of all possible flat properties. Essentially I am trying to mimic the functionality of json2csv, except that for array-valued properties, I don't split it into multiple columns, but store the entire array string as a value in CSV.
How do I accomplish this using Python or .NET?
This works. Might need to expand upon it a bit more to drill through arrays, etc. Uses Newtonsoft's JSON library, and assumes the JSON string is an object, not an array or a primitive (or anything else)
void Main()
{
var obj = JsonConvert.DeserializeObject(jsonStr) as JObject;
var props = GetPropPaths(string.Empty, obj);
props.Dump();
}
private IEnumerable<Tuple<string, string>> GetPropPaths(string currPath, JObject obj)
{
foreach(var prop in obj.Properties())
{
var propPath = string.IsNullOrWhiteSpace(currPath) ? prop.Name : currPath + "." + prop.Name;
if (prop.Value.Type == JTokenType.Object)
{
foreach(var subProp in GetPropPaths(propPath, prop.Value as JObject))
yield return subProp;
} else {
yield return new Tuple<string, string>(propPath, prop.Value.ToString());
}
}
}
For your above json, it gives the following:
I have a datatable that is being populated via serverside ajax. I need to pass variables to the serverside C# method. The variables are not getting to the serverside.
I've tried a few different approaches. It should be pretty easy.
var tblApplication = $('#tblApplication').DataTable({
"ajax": {
"url": '../../Search/ApplicationList',
"type": 'POST',
"data":{
yearh: 2014,
make: ''
}
},
"autoWidth": false,
"fnRowCallback": function (nRow, aData, iDisplayIndex, iDisplayIndexFull) {
$(nRow).addClass("parent");
return nRow;
},
"order": [[1, "desc"]],
"deferRender": true,
"columns": [
{
"title": '',
"class": 'details-control',
"orderable": false,
"data": null,
"defaultContent": '<img src="../../assets/images/details_open.png" />'
},
{ "title": 'ApplicationId', "data": 'ApplicationId', "visible": false },
{ "title": 'Year', "data": 'Year' },
{ "title": 'Make', "data": 'Make' },
{ "title": 'Model', "data": 'Model' },
{ "title": 'Qualifier', "data": 'Qualifier' },
{ "title": 'Axle', "data": 'Axle' },
{ "title": 'Pad Set', "data": 'PadSet' },
{ "title": 'Side', "data": 'Side' },
{ "title": 'Part List', "data": 'PartListId' }
]
});
[HttpPost]
public JsonResult ApplicationList(int year = 0, string make = null )
{
}
According to datatables.js reference you need to extend "data" something like following to make it work;
$('#example').dataTable( {
"ajax": {
"url": "data.json",
"data": function ( d ) {
return $.extend( {}, d, {
"extra_search": $('#extra').val(),
"year": 2014,
"make": 'Nissan'
} );
}
}
} );
For further documentation please see the this. Hope this helps.