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"
}
]
Related
Dynamics 365 v9.1.
How to get the non global optionset values metadata via REST API?
My query: https://mycompany.com/MyCompany/api/data/v9.1/EntityDefinitions(LogicalName='lead')/Attributes(LogicalName='statuscode')
My C# code:
public static HttpClient CreateCrmHttpClient(string domain, string crmWebApiUrl, string authType, string crmLogin,
string crmPassword, Guid? callerId)
{
var uri = new Uri(crmWebApiUrl);
var credentialsCache = new CredentialCache
{{uri, authType, new NetworkCredential(crmLogin, crmPassword, domain)}};
var handler = new HttpClientHandler {Credentials = credentialsCache};
var httpClient = new HttpClient(handler) {BaseAddress = uri, Timeout = new TimeSpan(0, 0, 60)};
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
httpClient.DefaultRequestHeaders.Add("OData-MaxVersion", "4.0");
httpClient.DefaultRequestHeaders.Add("OData-Version", "4.0");
httpClient.DefaultRequestHeaders.Add("Prefer", "odata.include-annotations=\"*\"");
if (callerId != null)
{
httpClient.DefaultRequestHeaders.Add("MSCRMCallerID", callerId.Value.ToString());
}
return httpClient;
}
...
public string RetrieveAttributeMetadata(string entityName, string attributeName)
{
var httpClient = CrmClientHelper.CreateCrmHttpClient(Domain, CrmWebApiUrl, AuthType, CrmLogin,
CrmPassword, CallerId);
var query = $"EntityDefinitions(LogicalName='{entityName}')/Attributes(LogicalName='{attributeName}')";
var response = httpClient.GetAsync(query).Result;
return response.Content.ReadAsStringAsync().Result;
}
Below the result, but it doesn't contain OptionSet values metadata. How to get it?:
{
"#odata.context": "https://mycompany.com/MyCompany/api/data/v9.1/$metadata#EntityDefinitions('lead')/Attributes/Microsoft.Dynamics.CRM.StatusAttributeMetadata/$entity",
"#odata.type": "#Microsoft.Dynamics.CRM.StatusAttributeMetadata",
"DefaultFormValue": -1,
"AttributeOf": null,
"AttributeType": "Status",
"ColumnNumber": 54,
"DeprecatedVersion": null,
"IntroducedVersion": "5.0.0.0",
"EntityLogicalName": "lead",
"IsCustomAttribute": false,
"IsPrimaryId": false,
"IsValidODataAttribute": true,
"IsPrimaryName": false,
"IsValidForCreate": true,
"IsValidForRead": true,
"IsValidForUpdate": true,
"CanBeSecuredForRead": false,
"CanBeSecuredForCreate": false,
"CanBeSecuredForUpdate": false,
"IsSecured": false,
"IsRetrievable": true,
"IsFilterable": false,
"IsSearchable": false,
"IsManaged": true,
"LinkedAttributeId": null,
"LogicalName": "statuscode",
"IsValidForForm": true,
"IsRequiredForForm": false,
"IsValidForGrid": true,
"SchemaName": "StatusCode",
"ExternalName": null,
"IsLogical": false,
"IsDataSourceSecret": false,
"InheritsFrom": null,
"CreatedOn": "1900-01-01T00:00:00+03:00",
"ModifiedOn": "2022-04-23T06:20:18+03:00",
"SourceType": null,
"AutoNumberFormat": null,
"MetadataId": "ed2b31c0-723f-447b-ac19-7fee763f3c8c",
"HasChanged": null,
"AttributeTypeName": {
"Value": "StatusType"
},
"Description": {
"LocalizedLabels": [
{
"Label": "Выберите состояние интереса.",
"LanguageCode": 1049,
"IsManaged": true,
"MetadataId": "8999f6ca-2241-db11-898a-0007e9e17ebd",
"HasChanged": null
}
],
"UserLocalizedLabel": {
"Label": "Выберите состояние интереса.",
"LanguageCode": 1049,
"IsManaged": true,
"MetadataId": "8999f6ca-2241-db11-898a-0007e9e17ebd",
"HasChanged": null
}
},
"DisplayName": {
"LocalizedLabels": [
{
"Label": "Причина состояния",
"LanguageCode": 1049,
"IsManaged": true,
"MetadataId": "8899f6ca-2241-db11-898a-0007e9e17ebd",
"HasChanged": null
}
],
"UserLocalizedLabel": {
"Label": "Причина состояния",
"LanguageCode": 1049,
"IsManaged": true,
"MetadataId": "8899f6ca-2241-db11-898a-0007e9e17ebd",
"HasChanged": null
}
},
"IsAuditEnabled": {
"Value": true,
"CanBeChanged": true,
"ManagedPropertyLogicalName": "canmodifyauditsettings"
},
"IsGlobalFilterEnabled": {
"Value": false,
"CanBeChanged": true,
"ManagedPropertyLogicalName": "canmodifyglobalfiltersettings"
},
"IsSortableEnabled": {
"Value": false,
"CanBeChanged": true,
"ManagedPropertyLogicalName": "canmodifyissortablesettings"
},
"IsCustomizable": {
"Value": true,
"CanBeChanged": false,
"ManagedPropertyLogicalName": "iscustomizable"
},
"IsRenameable": {
"Value": true,
"CanBeChanged": false,
"ManagedPropertyLogicalName": "isrenameable"
},
"IsValidForAdvancedFind": {
"Value": true,
"CanBeChanged": true,
"ManagedPropertyLogicalName": "canmodifysearchsettings"
},
"RequiredLevel": {
"Value": "None",
"CanBeChanged": true,
"ManagedPropertyLogicalName": "canmodifyrequirementlevelsettings"
},
"CanModifyAdditionalSettings": {
"Value": true,
"CanBeChanged": false,
"ManagedPropertyLogicalName": "canmodifyadditionalsettings"
}
}
You need to expand them, following your query it should be
https://mycompany.com/MyCompany/api/data/v9.1/EntityDefinitions(LogicalName='lead')/Attributes(LogicalName='statuscode')?$expand=OptionSet
the result will contain the optionset values.
This approach works for optionsets (choice), multiselect optionsets (choices), statecode, statuscode and boolean fields.
I'm getting the following error. I know there are similar answered questions but I have tried to use those answers but I am still unable to see my error.
Error Message:
I have the correct amount of columns in the table and in the request. I have checked for typo's. I am using a video as a guide and I have done everything exactly the same, yet mine doesn't work
code:
<table id="myTable">
<thead>
<tr>
<th>Title</th>
<th>FirstName</th>
<th>Surname</th>
<th>Address1</th>
<th>Address2</th>
<th>Town</th>
<th>Account1</th>
<th>Account2</th>
</tr>
</thead>
</table>
#section mydataTable{
<script type="text/javascript" charset="utf8"
src="https://cdn.datatables.net/1.10.25/js/jquery.dataTables.js"></script>
<script>
$(document).ready(function () {
$('#myTable').DataTable(
{
"processing": true,
"filter": false,
"serverSide": true,
"paging": false,
"responsive": true,
"ajax":
{
"url": "#Url.Action("LoadData")",
"datatype": "json",
"type": "POST",
},
"columnDefs": [
{ "defaultContent": "-", "targets": "_all" },
{ "width": "auto", "targets": 0, "orderable": false },
{ "width": "auto", "targets": 1, "orderable": false },
{ "width": "auto", "targets": 2, "orderable": false },
{ "width": "auto", "targets": 3, "orderable": false },
{ "width": "auto", "targets": 4, "orderable": false },
{ "width": "auto", "targets": 5, "orderable": false },
{ "width": "auto", "targets": 6, "orderable": false },
{ "width": "auto", "targets": 7, "orderable": false },
],
"columns": [
{ "data": "Title", "name": "CTitle" },
{ "data": "FirstName", "name": "CFirstName" },
{ "data": "Surname", "name": "CSurname" },
{ "data": "Address1", "name": "CAddress1" },
{ "data": "Address2", "name": "CAddress2" },
{ "data": "Town", "name": "CTown" },
{ "data": "Account1", "name": "CAccount1" },
{ "data": "Account2", "name": "CAccount2" },
],
});
});
</script>
}
C#:
public JsonResult LoadData()
{
IEnumerable<DeathClaims> deathclaims = GetDc();
return Json(new { data = deathclaims, recordsFiltered = deathclaims.Count(), recordsTotal = deathclaims.Count() });
}
private IEnumerable<DeathClaims> GetDc()
{
List<DeathClaims> deathlist = new List<DeathClaims>()
{
new DeathClaims {
Title = "Mr",
FirstName = "Michael",
Surname = "Smith",
Address1 = "132 Spalding Road",
Address2 = "TS252JP",
Town = "Hartlepool",
Account1 = "Current Account 1.0%",
Account2 = "Super Saver 3.0%"},
new DeathClaims {
Title = "Mr",
FirstName = "Steve",
Surname = "Smith",
Address1 = "1 Something Close",
Address2 = "TS273QQ",
Town = "Hartlepool",
Account1 = "Current Account 1.0%",
Account2 = "Super Saver 2.0%"}
};
return deathlist;
}
None of the data is getting through - I'm sure it's a small issue but I just cant see it
Thank you in advance for your help
Thanks
For DataTables plugin to work at server-side. You need to return the JSON in LoadData() method as follow i.e.
...
// Initialization.
string search = Request.Form.GetValues("search[value]")[0];
string draw = Request.Form.GetValues("draw")[0];
string order = Request.Form.GetValues("order[0][column]")[0];
string orderDir = Request.Form.GetValues("order[0][dir]")[0];
int startRec = Convert.ToInt32(Request.Form.GetValues("start")[0]);
int pageSize = Convert.ToInt32(Request.Form.GetValues("length")[0]);
...
// Process your code.
...
JsonResult result = this.Json(new { draw = Convert.ToInt32(draw), recordsTotal = totalRecords, recordsFiltered = recFilter, data = data }, JsonRequestBehavior.AllowGet);
return result;
...
The above properties of DataTables plugin are needed to be captured and maintained in your code by yourself.
{
"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
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
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: