C# Dynamic object has property with at symbol - c#

I have an object from my elasticsearch resultset;
that I'm iterating true this via this foreach:
foreach (Nest.IHit<dynamic> temp in result.Hits) {
}
one temp it's source looks like this (just rightclicked in visual studio and clicked on "copy value")
{{
"charSet": "UTF-8",
"executionTime": 927,
"parentUrl": "http://wfintranetdvlp.sidmar.be/sdg/",
"#timestamp": "2015-08-05T13:50:40.721Z",
"method": "GET",
"contentLength": 31575,
"mimeType": "text/html",
"text": "You are here: Home Productie Productie Productie Sidgal Sidgal 1 Campagneplan Dagverslag PamBrowser Sidgal 2 Campagneplan Dagverslag PamBrowser Sidgal 3 Campagneplan Dagverslag PamBrowser Alle Lijnen Stilstanden Productierapporten Autonoom Onderhoud JAP AO-zones Uitgevoerde AO-activiteit afgelopen jaar Kalender audits AO",
"title": "Productie",
"url": "http://wfintranetdvlp.sidmar.be/sdg/productie-2/",
"httpStatusCode": 200
}}
now in my code I can access the params like following temp.Source.title or temp.Source.url but when I want to access the #timestamp it returns null
any idea on how I can access the timestamp?

C# identifiers can not start with #. You're actually trying to access timestamp - the # is called the verbatim specifier, and it allows you to use keywords as identifiers, e.g. you can have a local named #this, which is actually the this identifier.
The only way would be to access the variable by name, something like yourvar["#timestamp"].

I deleted my original answer as I found this SO answer after trying something in my code for you.
The relevant code from the answer is this:
static object GetDynamicMember(object obj, string memberName)
{
var binder = Binder.GetMember(CSharpBinderFlags.None, memberName, obj.GetType(),
new[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) });
var callsite = CallSite<Func<CallSite, object, object>>.Create(binder);
return callsite.Target(callsite, obj);
}
It uses reflection to build up the call to get the value back, and the "#timestamp" can easily be passed in as a string.

Related

Removing object using JArrayItem.Remove("value") not working

I am trying to remove an object from an existing JArray, but when I run the method to delete the property from the JArray, the property still exists.
I tried simply using the code below
JObject rss1 = JObject.Parse(File.ReadAllText("online.json"));
JObject uList = (JObject)rss1["uList"];
JArray newOnline = (JArray)uList["online"];
newOnline.Remove("value");
The code above is almost directly copied and pasted from the Newtonsoft.Json documentation on modifying Json, but only changed to remove the new item instead of adding it. I've tried other post's solutions, but none of them work. Adding the field works as expected, but trying to remove it does not work whatsoever.
Below is the JSON file's contents
{
"uList": {
"placeHolder": "placeHolderValue",
"online": [
"value"
]
}
}
Below is the expected JSON output of the code being ran
{
"uList": {
"placeHolder": "placeHolderValue",
"online": [
]
}
}
And, below, is the actual output of the code being ran
{
"uList": {
"placeHolder": "placeHolderValue",
"online": [
"value"
]
}
}
Am I doing something wrong and not realizing it?
The problem you've got is that you don't actually have a string in your array, you have a JValue. The JArray.Remove method accepts a JToken, but the JToken produced by implicitly casing your string to a JValue is not equal to the one being removed.
There's one of two solutions that come to mind:
Find the item in the array and then pass that to the remove method:
JToken item = newOnline.FirstOrDefault(arr => arr.Type == JTokenType.String && arr.Value<string>() == "value");
newOnline.Remove(item);
Use the .Remove method of the array item to remove it from the array:
JToken item = newOnline.FirstOrDefault(arr => arr.Type == JTokenType.String && arr.Value<string>() == "value");
item?.Remove();
You can also remove JToken by querying value string present in online JArray.
Parse string to JObject:
JObject rss1 = JObject.Parse(File.ReadAllText("online.json"));
Now remove particular token using below query.
rss1.SelectTokens("$.uList.online[?(# == 'value')]").FirstOrDefault()?.Remove();
Try online

Getting error Newtonsoft.Json.Linq.JProperty cannot have multiple values when adding JToken

I have the following structure of additional information where I need to update the value of one of the tokens in the structure. The data is an array of JTokens with a parent called 'additionalFields' as follows:
{{"additionalFields":
[
{ "name": "NAME1", "value": "VALUE1" },
{ "name": "NAME2", "value": "VALUE2" },
{ "name": "NAME3", "value": "VALUE3" },
{ "name": "NAME4", "value": "VALUE4" }
]}
I'm trying to update the value of one of the tokens e.g. to change VALUE1 to VALUE10.
Once I have located the token I need to update my code removes it as follows.
additionalField.Remove();
I then create a new token to replace the one I have removed (containing the new value) using the following functions.
public static JToken CreateNewToken(string name, string value)
{
var stringToken = CreateNewStringToken(name, value);
var token = JToken.Parse(stringToken);
return (JToken) token;
}
private static string CreateNewStringToken(string name, string value)
{
return $"{{\"name\":\"{name}\",\"value\":\"{value}\"}}";
}
I then add the new token as follows.
additionalFields.AddAfterSelf(updatedToken);
Putting it all together we have the following
foreach (var additionalField in additionalFields)
{
//is this the key we are looking for?
var keyToken = additionalField.First;
if (keyToken?.First == null) continue;
if (string.Equals(keyToken.First.ToString(), "newname", StringComparison.CurrentCultureIgnoreCase))
{
//remove the current token
additionalField.Remove();
//add the updated token
var updatedToken = CreateNewToken("newname", "newvalue");
additionalFields.AddAfterSelf(updatedToken); <-- error occurs here!!
}
}
However after adding the token I'm getting the following error
Newtonsoft.Json.Linq.JProperty cannot have multiple values
I can see in the debugger that the token has been removed (as the token.Count is reduced by 1) so cannot understand why I'm getting an error adding the replacement token.
I was able to reproduce your problem here: https://dotnetfiddle.net/JIVCVB
What is going wrong
additionalFields refers to the JArray of JObjects containing name and value JProperties. You are looping through this JArray to try to find the first JObject having a name property with a certain value, and when you find it you attempt to replace the JObject with a whole new JObject. You successfully remove the old JObject from the JArray, but when you are doing AddAfterSelf to insert the new JObject, you are referencing additionalFields (plural) not additionalField (singular). Recall that additionalFields is the JArray. So you are saying that you want to add the new JObject after the array. The array's parent is a JProperty called additionalFields. A JProperty can only have one value, so AddAfterSelf fails with the error you see.
How to fix your code
I think what you intended to do was additionalField.AddAfterSelf(updatedToken). However, this, too, will fail, for a different reason: you already removed the additionalField from the JArray at that point, so it no longer has a parent context. You would need to AddAfterSelf before you remove the item you are trying to insert after. If you fix that, you still have another problem: your loop doesn't break out after you've done the replacement, so then you will get an error about modifying the collection while looping over it.
Here is the relevant section of code with the corrections:
if (string.Equals(keyToken.First.ToString(), "NAME1", StringComparison.CurrentCultureIgnoreCase))
{
//add the updated token
var updatedToken = CreateNewToken("newname", "newvalue");
additionalField.AddAfterSelf(updatedToken);
//remove the current token
additionalField.Remove();
// we found what we were looking for so no need to continue looping
break;
}
Fiddle: https://dotnetfiddle.net/KcFsZc
A simpler approach
You seem to be jumping through a lot of hoops to accomplish this task. Instead of looping, you can use FirstOrDefault to find the object you are looking for in the array. Once you've found it, you don't need to replace the whole object; you can just update the property values directly.
Here's how:
var rootObject = JToken.Parse(json);
// Get a reference to the array of objects as before
var additionalFields = rootObject["additionalFields"];
// Find the object we need to change in the array
var additionalField = additionalFields.FirstOrDefault(f =>
string.Equals((string)f["name"], "NAME1", StringComparison.CurrentCultureIgnoreCase);
// if the object is found, update its properties
if (additionalField != null)
{
additionalField["name"] = "newname";
additionalField["value"] = "newvalue";
}
Working demo: https://dotnetfiddle.net/ZAKRmi

Newtonsoft Get sub key from json without indexer syntax

I got a json with some nested object, for example:
{
"OrganizationData": {
"Org1": {
"Name": "Rega And Dodli",
"EmployessNum": "100000000"
},
"Org2": {
"Name": "Sami And Soso",
"EmployessNum": "2"
}
}
}
I want to get for example the value for the "Name" of "Org1".
I know I can do something like this:
var rss = JObject.Parse(mystring);
var value = rss["OrganizationData"]["Org1"]["Name"];
My question is if it's possible to replace the multiple indexers part (["OrganizationData"]["Org1"]["Name"]) with a single indexer (or something else which is not an indexer) with a single string which is composed of all 3 keys and still get the same value?
For example something like:
var rss = JObject.Parse(mystring);
var value = rss["OrganizationData:Org1:Name"];
I remember there's something with ":" but this one I tried in the example above did not work.
You can use the JObject.SelectToken method, using period (.) as the property path delimiter. For example:
var value = rss.SelectToken("OrganizationData.Org1.Name");

Addressing JSON in C# dynamically

I wish to write some C# which allows the client to provide a JSON string and query string. The query string would then be used to address values in the JSON object.
For example, if I had this JSON:
{
"head": "big",
"fingers": [
"one", "thumb",
"two", "ring"
],
"arm": {
"elbow", "locked"
}
}
And this query string:
"fingers.two"
I would want to return the value "ring".
Is this (or something like it) possible in C#?
I have tried using the ExpandoObject class, but this does not allow dynamic runtime inspection:
var json = JsonConvert.DeserializeObject<ExpandoObject>(jsonStr);
As far as I can tell, the discovery of values on the json variable needs to be done at code time, rather than runtime, which means I cannot dynamically find values being queried for.
JSONPath does this
Assuming the following JSON (fixed a few syntax errors in the original)
{
"head": "big",
"fingers": {
"one":"thumb",
"two":"ring"
},
"arm": {
"elbow": "locked"
}
}
And this query
MyJObjectOrToken.SelectToken("fingers.two")
You will get the following output:
[
"ring"
]
It should be trivial then to extract the value as a string using JSON.Net methods and return the result to your user.
Support for JSONPath is built into JSON.Net
https://www.newtonsoft.com/json/help/html/SelectToken.htm

How to get the __type key in json to be first for DataContractJsonSerializer

[{"id":"PROCESS_ROOT_NODE","text":"TEMPLATE - 3333(2)","icon":"fa fa-list fa-color-graylt","li_attr":{"id":"PROCESS_ROOT_NODE","__type":"li_attr:#SomeNamespace.JsonDataContractClasses","class":" ps_node_li "}}]
I slimmed the object down alot.
Basically when the '__type' is not in the first position, before 'id'. The deserialize will throw an error.
I have all the DataContract stuff setup correctly, with known types.
I've tested in a console app, serializing, then taking that string back thru the deserialize and it works perfectly. The only difference is the location of the '__type'. This is a known MS issue.
Documented at https://msdn.microsoft.com/en-us/library/bb412170(v=vs.110).aspx
Tried a string replace, which does work. and the DataContractJsonSerializer did not care if '__type' key was in there twice.
content = content.Replace("\"li_attr\":{", "\"li_attr\":{\"__type\":\"li_attr:#Payce.Common.AnonymousClasses.JsonDataContractClasses\",");
Just looking for the best way to move the __type to the first position.
You can use Json.Net to manipulate your json
var jArr = JArray.Parse(jsonstring);
var attrs = jArr.Select(x => x["li_attr"]).ToList();
attrs.ForEach(attr =>
{
var type = attr["__type"].Parent;
type.Remove();
(attr as JObject).AddFirst(type);
});
var newjson = jArr.ToString(Newtonsoft.Json.Formatting.Indented);
Output of this code is
[
{
"id": "PROCESS_ROOT_NODE",
"text": "TEMPLATE - 3333(2)",
"icon": "fa fa-list fa-color-graylt",
"li_attr": {
"__type": "li_attr:#SomeNamespace.JsonDataContractClasses",
"id": "PROCESS_ROOT_NODE",
"class": " ps_node_li "
}
}
]
But I would recommend to use Json.Net all the way instead of just converting your json to the desired format.
Besides the string replacement.
I used an answer from Dave R - stack overflow
Use the JSON.stringify(obj, replacer array) replacer method.
var json = JSON.stringify(o, ['__type', 'id', 'parent', 'text', 'type', 'children', 'data', 'li_attr', 'a_attr', 'state', 'class', 'descr', 'display_priority', 'action_area_id', 'action_user_type_id']);
Its a little bit of a pain to list all the keys, but it also acts like a filter, so i only return what i need as well.
And since i put '__type' first, in all the objects and sub objects, this key was listed first after the stringify.

Categories

Resources