I'm struggling to deserialzer the JSON snippet below due to the different fields being returned, for example in config, it works ok with config.on, as this is in each config block, but as they others are all different, how do I get round this? The JSON returns all the different sensors connected so with different fields.
dynamic obj = JsonConvert.DeserializeObject(response);
//
foreach (var item in obj)
{
string temperature = item.ToString(); //this shows the full object in text
// Debug.WriteLine(temperature); //this shows the full object in text
dynamic group = item;
string idstring = group.Name.ToString();
foreach (var prop in group)
{
string name = prop.name;
string temp = prop.config.on;
//string temp = prop.state.lastupdated;
//string temp = prop.swversion;
//Debug.WriteLine(temp);
string namestring = name.ToString();
string tempstring = temp.ToString();
arr[0] = idstring.ToLower();
arr[1] = namestring.ToLower();
arr[2] = tempstring.ToLower();
with part of the JSON response
{
"1": {
"state": {
"daylight": true,
"lastupdated": "2017-03-10T07:01:00"
},
"config": {
"on": true,
"configured": true,
"sunriseoffset": 30,
"sunsetoffset": -30
},
"name": "Daylight",
"type": "Daylight",
"modelid": "PHDL00",
"manufacturername": "Philips",
"swversion": "1.0"
},
"2": {
"state": {
"temperature": 1830,
"lastupdated": "2017-03-10T08:11:51"
},
"config": {
"on": true,
"battery": 100,
"reachable": true,
"alert": "none",
"ledindication": false,
"usertest": false,
"pending": [
},
"name": "Hall",
"type": "ZLLPresence",
"modelid": "SML001",
"manufacturername": "Philips",
"swversion": "6.1.0.18912",
"uniqueid": "00:17:88:01:02:01:8b:be-02-0406"
},
"4": {
"state": {
"lightlevel": 12270,
"dark": true,
"daylight": false,
"lastupdated": "2017-03-10T08:14:28"
},
"config": {
"on": true,
"battery": 100,
"reachable": true,
"alert": "none",
"tholddark": 16011,
"tholdoffset": 7000,
"ledindication": false,
"usertest": false,
"pending": [
]
},
"name": "Hue ambient light sensor 1",
"type": "ZLLLightLevel",
"modelid": "SML001",
"manufacturername": "Philips",
"swversion": "6.1.0.18912",
"uniqueid": "00:17:88:01:02:01:8b:be-02-0400"
},
"5": {
"state": {
"temperature": 1919,
"lastupdated": "2017-03-10T08:12:50"
There is no need to be extra creative with converting missing properties to String multiple times. So instead of doing this:
string namestring = name.ToString();
string tempstring = temp.ToString();
You simply check for null condition up front (if you need) and take action accordingly. Json.NET will not throw an exception if a property is missing, so you can try to get all fields that you need and then execute business logic accordingly:
string name = prop.name;
string temp = prop.config.on;
string battery = prop.config.battery;
if (battery == null)
// maybe throw exception, maybe assign default value; whatever fits you
Related
For example we have following document in elastic:
{
"name": "Bob",
"age": "22",
"phrase": "ohohoho",
"date": "2022-10-20T00:00:00Z"
}
string phrase ;
DateTime? date;
Then we want put following:
{
"name": "not Bob",
"age": "22",
"phrase": null,
"date": null
}
in c#:
var updateRequest = new UpdateRequest<T, T>(entity)
{
ScriptedUpsert = true,
Script = new InlineScript(
$"if (someCondition) {{ctx._source.putAll(params.entity);}} else {{ctx.op = \"noop\";}}")
{
Lang = "painless",
Params = new Dictionary<string, object>() { { "entity", entity } },
},
Upsert = Activator.CreateInstance<T>()
};
but in the end it will not update phrase and date.
It makes following request:
POST /myIndex/_update/b90278fd-1a66-40bf-b775-d076122c6c02
{
"script": {
"source": ""if (someCondition) {{ctx._source.putAll(params.entity);}} else {{ctx.op = \"noop\";}}"",
"lang": "painless",
"params": {
"entity": {
"name": "",
"age": 22
}
}
},
"upsert": {
"age": 0
}
}
Idk why but it skips all fields with null.
How to update nullable fields to null?
NEST does not support sending null values by default.
You can have a check in script such that if a value is not passed then you can remove it from document.
var updateRequest = new UpdateRequest<T, T(entity)
{
ScriptedUpsert = true,
Script = new InlineScript($"if (params.entity.phrase==null)ctx._source.remove('phrase');")
{
Lang = "painless",
Params = new Dictionary<string, object>() { { "entity", entity } },
},
Upsert = Activator.CreateInstance<T>()
};
You can check for more details here
I'm looking for a way to find a specific json value by its name and set its value to null. The construction of the json file can be anything, it's not always the same.
Let's say the json looks like this:
[
{
"id": "1111",
"email": "email#email.com",
},
{
"id": "2222",
"email": "email2#email2.com",
}]
The result I'm looking for is this:
[
{
"id": "1111",
"email": null,
},
{
"id": "2222",
"email": null,
}]
When the object is more complicated it should work too.
{
"reservations": [
{
"id": "111",
"bookingId": "",
"status": "",
"checkInTime": "",
"checkOutTime": "",
"property": {
"id": "",
"code": "",
"name": "",
},
"primaryGuest": {
"firstName": "",
"middleInitial": "",
"lastName": "",
"email": "email#email.com",
"phone": "",
"address": {
"addressLine1": "",
"postalCode": "",
"city": "",
"countryCode": ""
}
},
"booker": {
"firstName": "",
"middleInitial": "",
"lastName": "",
"email": "email2#email.com",
"phone": ""
}
}]}
I've tried to use JArray, JObject classes etc, but it only works if the propety["email"] is the first child, not deeper. Not sure how to accomplish this.
private JObject HashSensitiveData(JContainer jContainer)
{
if (!jContainer.Descendants().Any())
{
return null;
}
var objects = jContainer.Descendants().OfType<JObject>();
foreach (var property in objects)
{
foreach (var emailProperty in property.Properties().Where(x => x.Name.CaseInsensitiveContains(LoggerHashedProperties.Email.ToString())))
{
var email = emailProperty.Value.ToString();
property[emailProperty.Name] =null
}
}
return HashSensitiveData(jContainer);
}
Using NewtonSoft, I once made a flatten function to analyse json files of any depth:
IEnumerable<JProperty> Flatten(JToken token)
{
return token.Children<JProperty>().Concat(
token.Children().SelectMany(t => Flatten(t)))
.Where(t => t.Value is JValue);
}
It returns a flat listing of all "endpoint" JsonPropertys in a file (say: all "xyx" : primitive value entries). Using it you can simply deserialize your Json, find all "email" properties and set their value to null:
var jobj = JsonConvert.DeserializeObject<JObject>(getJson());
var flattened = Flatten(jobj);
foreach (var jprop in flattened.Where(t => t.Name == "email"))
{
jprop.Value = null;
}
var json = JsonConvert.SerializeObject(jobj).Dump();
Result for the "more complicated" json (with one deeper email added to make it more fun):
{
"reservations": [
{
"id": "111",
"bookingId": "",
"status": "",
"checkInTime": "",
"checkOutTime": "",
"property": {
"id": "",
"code": "",
"name": ""
},
"primaryGuest": {
"firstName": "",
"middleInitial": "",
"lastName": "",
"email": null,
"phone": "",
"address": {
"email": null,
"addressLine1": "",
"postalCode": "",
"city": "",
"countryCode": ""
}
},
"booker": {
"firstName": "",
"middleInitial": "",
"lastName": "",
"email": null,
"phone": ""
}
}
]
}
With System.Text.Json and Utf8JsonWriter you can process and write your JSON recursively:
public static void RemoveContent(JsonElement element, Utf8JsonWriter writer)
{
// Current element is an array, so we have to iterate over all elements.
if (element.ValueKind == JsonValueKind.Array)
{
writer.WriteStartArray();
foreach (var e in element.EnumerateArray())
{
RemoveContent(e, writer);
}
writer.WriteEndArray();
}
// Current element is an object, so we have to process all properties.
else if (element.ValueKind == JsonValueKind.Object)
{
writer.WriteStartObject();
foreach (var e in element.EnumerateObject())
{
writer.WritePropertyName(e.Name);
// * Process specific elements. ************************************************
if (e.Name == "email") { writer.WriteNullValue(); }
else RemoveContent(e.Value, writer);
}
writer.WriteEndObject();
}
// We are at the leaf (a string property, ...) and we write this as it is.
else
{
element.WriteTo(writer);
}
}
Usage:
using var stream = new MemoryStream();
// We have to flush writer before reading it's content.
using (var writer = new Utf8JsonWriter(stream, new JsonWriterOptions { SkipValidation = true }))
{
var element = JsonDocument.Parse(jsonStr).RootElement;
RemoveContent(element, writer);
}
// Stream contains a byte array with UTF-8 content.
// If you want a string, you can use UTF8 decoding.
var json = System.Text.Encoding.UTF8.GetString(stream.ToArray());
Console.WriteLine(json);
Here, I am trying to create a JSON using using Newtonsoft.Json; libraries. I came up with a function which partially does my job and when the same function is called, my previous data on the JObject is lost. I am new to this items JProperty, JObject, JArray, JToken however managed to come up with this code.
The parent is initiated in the function itself and when the function is recursively called its initiated and cleaned up again. So I added another JObject head. Making it more complicated.
string statment = "(Entity.Country = 'USA' AND Entity.ShortName = 'Adele' AND (Entity.CIFNumber = '12345' OR Statement.StatementYear = '2015'))";
public static JObject ConvertToJsonObject(string text)
{
JObject parent = new JObject();
string bracketContents = getWhatsInsideBrackets(text);
parent.Add(new JProperty("operator", ReturnOperator(text)));
parent.Add(new JProperty("rules"));
string[] operators = splitWithOperator(bracketContents);
List<JObject> req = new List<JObject>();
for (int i = 0; i < splitWithOperator(bracketContents).Length; i++)
{
if (!checkIfBracketsExists(operators[i].Trim()))
{
req.Add(GetEachCondition(operators, i));
}
else if (checkIfBracketsExists(operators[i]))
{
parent["rules"] = new JArray(ConvertToJsonObject(operators[i]));
head = parent;
parent["rules"] = (ConvertToJsonObject(operators[i]));
}
}
parent["rules"] = new JArray(req);
head["rules"] = parent;
return parent;
}
I am trying to achieve this JSON output: (Only trying to achieve the key, value of DataSetCommonQuery, dont worry about the other keys on the json)
{
"context": {
"wfId": "00000000-0000-0000-0000-000000000000",
"taskId": "00000000-0000-0000-0000-000000000000"
},
"payLoad": {
"DataSetCommonQuery": {
"operator": "AND",
"rules": [
{
"field": "ENTITY.CIFNumber",
"condition": "<>",
"value": "3123"
},
{
"field": "ENTITY.Country",
"condition": "LIKE",
"value": "USA"
},
{
"operator": "OR",
"rules": [
{
"field": "ENTITY.FYEMonth",
"condition": "=",
"value": "May"
},
{
"field": "STATEMENT.ProfitBeforeTax",
"condition": ">=",
"value": 123123
},
{
"field": "STATEMENT.NetSales",
"condition": "<=",
"value": 234234
},
{
"field": "STATEMENT.statementdatekey_",
"condition": "=",
"value": "2019-07-01 12:00:00"
}
]
}
]
},
"PeerType": "DEFAULT",
"Name": "API TEST",
"Description": "API TEST",
"BmkPeerFormatId": "DBBmk",
"OperationType": "Create"
}
}
I am automating updates to work item hours but changes to Status are ignored. I'd like to set status from "Active" to "Resolved".
I have found information stating that you also need to set a "Reason" if you are changing the status but my code isn't changing Reason or Status although all the other field updates are working. I suspect it is because the Status field is read-only but we are unable to find a rule that makes it so (we're using the CMMI template):
Can someone tell me if the problem is the setup in dev ops or if it is my code (or something else)?
//Executing from LINQPad, no need to mention the blocks on async....
WorkItem targetWorkItem = client.GetWorkItemAsync(123456).Result;
JsonPatchDocument patchDocument = new JsonPatchDocument();
patchDocument.Add(
new JsonPatchOperation()
{
Operation = Operation.Replace,
Path = "/fields/Microsoft.VSTS.Scheduling.CompletedWork",
Value = 123
}
);
patchDocument.Add(
new JsonPatchOperation()
{
Operation = Operation.Replace,
Path = "/fields/Microsoft.VSTS.Scheduling.RemainingWork",
Value = 0
}
);
/*
These don't work! I think because "Reason" field is read only
*/
patchDocument.Add(
new JsonPatchOperation()
{
Operation = Operation.Add, //Tried Replace as well as Add
Path = "/Fields/System.Reason",
Value = "Complete and Requires Review/Test"
}
patchDocument.Add(
new JsonPatchOperation()
{
Operation = Operation.Add, //Tried Replace as well as Add
Path = "/Fields/System.State",
Value = "Resolved"
}
);
//Succeeds for any field except Status and Reason
WorkItem result = client.UpdateWorkItemAsync(patchDocument, 123456).Result;
Namespaces used:
Microsoft.TeamFoundation.WorkItemTracking.WebApi
Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models
Microsoft.VisualStudio.Services.Common
Microsoft.VisualStudio.Services.WebApi
Microsoft.VisualStudio.Services.WebApi.Patch
Microsoft.VisualStudio.Services.WebApi.Patch.Json
You have a syntax error, you should write /fields/System.State with f and not Fields with F.
And change the state is enough, the reason will be changed automatically.
Your Json should end up looking like this:
{
"id": xx,
"rev": yy,
"fields": [{
"field": {
"refName": "System.State"
},
"value": "Resolved"
},
{
"field": {
"refName": "System.Reason"
},
"value": "Status Reason"
},
{
"field": {
"refName": "Microsoft.VSTS.Common.ActivatedBy"
},
"value": null
},
{
"field": {
"refName": "Microsoft.VSTS.Common.ActivatedDate"
},
"value": null
},
{
"field": {
"refName": "Microsoft.VSTS.Common.ResolvedDate"
},
"value": "2014-08-25T19:14:04.594Z"
},
{
"field": {
"refName": "Microsoft.VSTS.Common.ResolvedBy"
},
"value": "User Name"
},
{
"field": {
"refName": "Microsoft.VSTS.Common.ResolvedReason"
},
"value": "Resolved Reason"
},
{
"field": {
"refName": "Microsoft.VSTS.Common.ClosedDate"
},
"value": <null or "2014-08-25T19:14:04.594Z">
},
{
"field": {
"refName": "Microsoft.VSTS.Common.ClosedBy"
},
"value": <null, "John Doe">
}]
}
I'm trying to get spuCode from this JSON:
{
"msg": "success",
"state": 0,
"data": {
"result": {
"spuCode": "541426110605",
"productName": "纯黑色斜纹面料铅笔裤百搭大码小脚裤弹力打底裤休闲裤子女",
"productTitle": null,
"spuImgs": [
"https://cbu01.alicdn.com/img/ibank/2016/276/468/3618864672_1742354982.jpg",
"https://cbu01.alicdn.com/img/ibank/2016/793/372/3617273397_1742354982.jpg",
"https://cbu01.alicdn.com/img/ibank/2016/726/552/3617255627_1742354982.jpg",
"https://cbu01.alicdn.com/img/ibank/2017/521/101/4624101125_1742354982.jpg",
"https://cbu01.alicdn.com/img/ibank/2017/070/749/4580947070_1742354982.jpg"
],
"upAndDown": 1,
"updateTime": 1537096913958,
"platform": "ALIBABA",
"skus": [
{
"skuCode": "3488434133172",
"sellPrice": 3900,
"sellableNum": 905,
"productProps": [
{
"propId": 7590793702270582000,
"valueId": 5453504708925905000,
"propName": "颜色",
"valueName": "纯黑色(相同面料)"
},
{
"propId": 9000005669393888000,
"valueId": 6217370164147047000,
"propName": "尺码",
"valueName": "XXL"
}
],
As you can see there is Parent data with child result so I added this code:
string sp = "";
var obj = Newtonsoft.Json.Linq.JObject.Parse(responseFromServer);
foreach (JObject child in obj["data"]["result"].OfType<JObject>())
{
sp = child["spuCode"].ToString();
MessageBox.Show(sp);
}
But it never triggers MessageBox.Show what am I missing here?
result isn't an array so there is no need for foreach.
Try SelectToken method like this:
var spuCode = (string)obj.SelectToken("data.result.spuCode");