JSON.net is stripping properties when iterating over them - c#

I have a json string that I'm parsing to a JObject using JObject.parse(string). When I do this all properties and objects inside that json string are present as evidenced by a ToString() method call. However as soon as I call an IEnumerable method such as
var _mapping = JObject.parse(json);
var _json = _mapping.ToString();
// _json == json at this poing other than some formating
var map = _mapping["c1-14.10.16"]["mappings"]["applog"]["properties"];
foreach(JToken v in map) {
string s = v.ToString();
// s != corresponding node string of json
}
many properties simply disappear from v and v's children.
Here is a short snippet of json that fails
{"c1-14.10.16":{"mappings":{"applog":{"properties":{"error":{"properties":{"error":{"properties":{"data":{"properties":{"DisablePrepareForRethrow":{"type":"string","index_analyzer":"standard"},"HelpLink.BaseHelpUrl":{"type":"string","index_analyzer":"standard"},"HelpLink.EvtID":{"type":"string","index_analyzer":"standard"},"HelpLink.EvtSrc":{"type":"string","index_analyzer":"standard"},"HelpLink.LinkId":{"type":"string","index_analyzer":"standard"},"HelpLink.ProdName":{"type":"string","index_analyzer":"standard"},"Microsoft.ServiceBus":{"type":"string","index_analyzer":"standard"},"length":{"type":"integer"}}}}}}}}}}}}
The only property in "c1-14.10.16".mappings.applog.properties.error.properties.error.properties.data.properties that is recognized is length. none of the other properties are recognized at all.
What am I doing wrong that makes it strip out the other properties?

The properties are there -- but at a much lower level than your code seems to expect. You can see this as follows:
public static void TestJsonParse(string json)
{
try
{
var _mapping = JObject.Parse(json);
var _json = _mapping.ToString();
// _json == json at this poing other than some formating
var map = _mapping["c1-14.10.16"]["mappings"]["applog"]["properties"];
map.WritePropertiesToConsole();
Debug.Assert(map.Count() == 1); // No assert because the properties aren't here.
var subMap = _mapping["c1-14.10.16"]["mappings"]["applog"]["properties"]["error"]["properties"]["error"]["properties"]["data"]["properties"];
subMap.WritePropertiesToConsole();
Debug.Assert(subMap.Count() == 8); // no assert - the properties are all here.
Debug.Assert(_mapping["c1-14.10.16"]["mappings"]["applog"]["properties"]["error"]["properties"]["error"]["properties"]["data"]["properties"]["Microsoft.ServiceBus"]["index_analyzer"].ToString() == "standard"); // no assert
}
catch (Exception ex)
{
Debug.Assert(false, ex.ToString()); // No exception, no assert.
}
}
public static void WritePropertiesToConsole(this JToken subMap)
{
int iToken = 0;
Console.WriteLine(string.Format("Tokens for {0}: {1} found", subMap.Path, subMap.Count()));
foreach (JToken v in subMap)
{
string s = v.ToString();
Console.WriteLine(string.Format("Token {0}: {1}", iToken++, s));
}
}
And here is the console output:
Tokens for c1-14.10.16.mappings.applog.properties: 1 found
Token 0: "error": {
"properties": {
"error": {
"properties": {
"data": {
"properties": {
"DisablePrepareForRethrow": {
"type": "string",
"index_analyzer": "standard"
},
"HelpLink.BaseHelpUrl": {
"type": "string",
"index_analyzer": "standard"
},
"HelpLink.EvtID": {
"type": "string",
"index_analyzer": "standard"
},
"HelpLink.EvtSrc": {
"type": "string",
"index_analyzer": "standard"
},
"HelpLink.LinkId": {
"type": "string",
"index_analyzer": "standard"
},
"HelpLink.ProdName": {
"type": "string",
"index_analyzer": "standard"
},
"Microsoft.ServiceBus": {
"type": "string",
"index_analyzer": "standard"
},
"length": {
"type": "integer"
}
}
}
}
}
}
}
Tokens for c1-14.10.16.mappings.applog.properties.error.properties.error.properties.data.properties: 8 found
Token 0: "DisablePrepareForRethrow": {
"type": "string",
"index_analyzer": "standard"
}
Token 1: "HelpLink.BaseHelpUrl": {
"type": "string",
"index_analyzer": "standard"
}
Token 2: "HelpLink.EvtID": {
"type": "string",
"index_analyzer": "standard"
}
Token 3: "HelpLink.EvtSrc": {
"type": "string",
"index_analyzer": "standard"
}
Token 4: "HelpLink.LinkId": {
"type": "string",
"index_analyzer": "standard"
}
Token 5: "HelpLink.ProdName": {
"type": "string",
"index_analyzer": "standard"
}
Token 6: "Microsoft.ServiceBus": {
"type": "string",
"index_analyzer": "standard"
}
Token 7: "length": {
"type": "integer"
}
The properties are there, at exactly the depth specified in the JSON string provided in the question. So JSON.net isn't stripping anything from the example provided. You can see the hierarchy clearly using http://jsonformatter.curiousconcept.com/:
If you change your problem description to be as follows: "given some JSON, how can I recursively find a child property named 'properties' with more than one child property", you can do it like this, adapted from here:
static IEnumerable<JToken> FindProperties(JToken root)
{
return root.WalkNodes().Where(n =>
{
var _parent = n.Parent as JProperty;
if (_parent != null && _parent.Name == "properties")
{
if (n.Count() > 1)
return true;
}
return false;
});
}
public static IEnumerable<JToken> WalkNodes(this JToken node)
{
if (node.Type == JTokenType.Object)
{
yield return (JObject)node;
foreach (JProperty child in node.Children<JProperty>())
{
foreach (var childNode in child.Value.WalkNodes())
yield return childNode;
}
}
else if (node.Type == JTokenType.Array)
{
foreach (JToken child in node.Children())
{
foreach (var childNode in child.WalkNodes())
yield return childNode;
}
}
}
and then, to test it:
var _properties = FindProperties(_mapping);
var list = _properties.ToArray();
Debug.Assert(list.Length == 1 && list[0] == subMap); // no assert

The problem appears to be that the json you posted, is invalid.
In an attempt to edit your post so that the json was more formatted, the site I used correctly identified errors in your JSON string.
Go here, and paste your JSON there and you'll see the problem:

Related

Not able to update a value from an array of JToken Type

I am trying to update a value which is a parameter in an array of JToken type. But I am getting the error below:
Set JArray values with invalid key value: "tagCategoryId". Int32 array index expected.
Below is my method:
JObject _asset;
JToken _tagCategoryObject ;
public void Test1()
{
var invalidTagCategoryId = "invalidTagCategoryId";
while (invalidTagCategoryId.Length <= 255) invalidTagCategoryId += invalidTagCategoryId;
_asset.TryGetValue("enumeratedTagCategories", out _tagCategoryObject);
if (_tagCategoryObject != null)
{
_tagCategoryObject["tagCategoryId"] = invalidTagCategoryId;
}
.....
}
Below is the json:
"enumeratedTagCategories": [
{
"tagCategoryId": "TagCategoryId",
"tagCategoryDisplayName": "TagCategoryDisplayName",
"version": "Version",
"tagValueIdsList": [
{
"displayName": "DisplayName1"
},
{
"displayName": "DisplayName2"
}
]
}
]
How can I assign a value to tagCategoryId ?
_tagCategoryObject is array so try this
var _tagCategoryObject = JObject.Parse(json)["enumeratedTagCategories"];
if (_tagCategoryObject != null)
{
_tagCategoryObject[0]["tagCategoryId"] = invalidTagCategoryId;
}
and fix you json by wrapping in {}
{ "enumeratedTagCategories": [
{
"tagCategoryId": "TagCategoryId",
"tagCategoryDisplayName": "TagCategoryDisplayName",
"version": "Version",
"tagValueIdsList": [
{
"displayName": "DisplayName1"
},
{
"displayName": "DisplayName2"
}
]
}
]
}

How to declare anonymous type for Json serialization, if one of the Json keys contains a dot?

I am serializing an anonymous object to use as a request message for an HTTP post request. The problem is that one of the JSON keys contains a dot in its name. VS throws out ''invalid anonymous type member declarator'' error.
return JsonConvert.SerializeObject(new
{
query = "something",
firstname.keyword = "xyz"
});
What can I do to solve this issue?
Edit: the real json request looks something like this, so I don't think I can use a dictionary:
{
"query": {
"bool": {
"must": [
{
"term": {
"firstname.keyword": ""
}
}
],
"must_not": [ ],
"should": [ ]
}
},
"from": 0,
"size": 10,
"sort": [ ],
"aggs": { }
}
Json can generally be represented using arrays, dictionaries and anonymous objects.
The first part of your Json can be generated as follows:
return JsonConvert.SerializeObject(new
{
query = new
{
#bool = new
{
must = new[]
{
new
{
term = new Dictionary<string, object>
{
["firstname.keyword"] = string.Empty,
}
}
}
}
}
});

Why does JSON.Net modify the original JObject?

I'm trying to understand why JSON.Net will modify the original JObject (formSchema) in two cases below where I'm adding JArray and JObject
to objects I've defined as vars.
I assume the two vars I created were standalone objects in their own right but it appears they are not.
In the first case, requiredItems is a JArray var, in which I want to add the token "certification" to formSchema, but I've placed the Add method
on requiredItems. Not formSchema.
In the second case, I add certifyProperty to formSchema using the Add method on consentProps. The property handily gets added to formSchema
without a direct reference to it.
Why is this? Are they linked in memory? Where in the JSON.Net docs is this explained? I cannot find it.
namespace JSONProps
{
class Program
{
static void Main(string[] args)
{
string jsonSchema = #"
{
""jsonSchema"": {
""type"": ""object"",
""title"": ""a title"",
""properties"": {
""consent"": {
""type"": ""object"",
""title"": ""Consent of Applicant"",
""required"": [
""applicantConsent""
],
""properties"": {
""applicantConsent"": {
""type"": ""boolean"",
""title"": ""I give my Consent"",
},
}
}
}
}
}
";
// First case
var formSchema = JObject.Parse(jsonSchema);
var requiredProps = formSchema["jsonSchema"]["properties"]["consent"]["required"] as JArray;
requiredProps.Add("certification");
// Second case
var consentProps = formSchema["jsonSchema"]["properties"]["consent"]["properties"] as JObject;
var certifyProperty = JObject.Parse(#" { ""type"" : ""boolean"", ""title"" : ""This is true."" } ");
consentProps.Add("certification", certifyProperty);
Console.WriteLine(formSchema.ToString());
}
}
}
$ dotnet run
{
"jsonSchema": {
"type": "object",
"title": "a title",
"properties": {
"consent": {
"type": "object",
"title": "Consent of Applicant",
"required": [
"applicantConsent",
"certification"
],
"properties": {
"applicantConsent": {
"type": "boolean",
"title": "I give my Consent"
},
"certification": {
"type": "boolean",
"title": "This is true."
}
}
}
}
}
}

How to update property value in JSON string?

Below is my JSON:
[
{
"name": "Node-1",
"flag": true,
"myObj": {
region: {
info: {
name: null,
infoFlag: false,
}
}
},
"nodes": [
{
"name": "Node-1-1",
"flag": true,
"myObj": {
region: {
info: {
name: "abc",
infoFlag: false,
}
}
},
"nodes": [
{
"name": "Node-1-1-1",
"flag": true,
"myObj": {
region: {
info: {
name: "xyz",
infoFlag: false,
}
}
},
"nodes": [
]
}
]
}
]
}
]
I want to update two properties of my above JSON string with following rule :
Flag : I want to blindly update this property to false.
infoFlag: If name property of info is null then I want to update infoFlag to true else false if it is not null.
So after updating my JSON with these rules I want to have that JSON as a string.
Note: I don't want to deserialize and then update property based on above two rules as because my JSON has lots of properties for which I don't want to create classes, so I am looking for something which will work without deserializing with class.
This is how I am trying to do it:
string json = "MyJson";
var temp = JArray.Parse(json);
temp.Descendants()
.OfType<JProperty>()
json = temp.ToString();
But here I am not getting as how to traverse my JSON recursively; as you can see I have recursive structure like below :
Node-1
Node-1-1
Node-1-1-1
Json.NET allows you to treat its internal objects representing the JSON content as dynamic, which makes the task in question no harder than using regular typed objects.
The only kind of tough problem is the recursive object structure (the nodes array), but that's not a JSON or dynamic specific problem, and can be solved in many ways - the obvious recursive method or with my preferable tree flattening enumeration (the Expand method from my answer to How to flatten tree via LINQ?).
With that being said, the solution could be like this:
var array = JArray.Parse(json);
var nodes = array.Cast<dynamic>().Expand(x => x.nodes);
foreach (var node in nodes)
{
node.flag = true;
var info = node.myObj.region.info;
info.infoFlag = (info.name == null);
}
var newJson = array.ToString();
If I have understood your needs this code is pretty verbose and not so elegant but works:
JArray temp = JArray.Parse(json);
foreach (JToken tk in temp.Descendants())
{
if (tk.Type == JTokenType.Property)
{
JProperty p = tk as JProperty;
if (p.Name == "flag")
{
if ((bool)p.Value.ToObject(typeof(bool)) == true)
p.Value = false;
}
if ((p.Name == "info") && p.HasValues)
{
bool flag = false;
foreach (JToken tkk in p.Descendants())
{
if (tkk.Type == JTokenType.Property)
{
JProperty pp = tkk as JProperty;
if ((pp.Name == "name") && (pp.Value.Type == JTokenType.Null))
{
flag = true;
}
if ((pp.Name == "infoFlag"))
{
pp.Value = (flag == true) ? true : false;
}
}
}
}
}
}
json = temp.ToString();
This is the resulting output:
[
{
"name": "Node-1",
"flag": false,
"myObj": {
"region": {
"info": {
"name": null,
"infoFlag": true
}
}
},
"nodes": [
{
"name": "Node-1-1",
"flag": false,
"myObj": {
"region": {
"info": {
"name": "abc",
"infoFlag": false
}
}
},
"nodes": [
{
"name": "Node-1-1-1",
"flag": false,
"myObj": {
"region": {
"info": {
"name": "xyz",
"infoFlag": false
}
}
},
"nodes": []
}
]
}
]
}
]

How can I merge two JObject? [duplicate]

This question already has answers here:
Merge two Json.NET arrays by concatenating contained elements
(3 answers)
Closed 7 years ago.
I have a first json:
{
"data": [{
"id": "id1",
"field": "field1"
}],
"paging": {
"prev": "link1",
}
}
and a second one:
{
"data": [{
"id": "id2",
"field": "field2"
}],
"paging": {
"prev": "link2",
}
}
and I want to merge/union the two Data array, such as:
{
"data": [{
"id": "id1",
"field": "field1"
},
{
"id": "id2",
"field": "field2"
}]
}
(I don't care about about paging right now).
How can I do it quick and easy? This is my try:
var final = JsonConvert.SerializeObject(new { data = json1["data"].Union(json2["data"]) }, Newtonsoft.Json.Formatting.Indented).ToString();
but an Exception is raised: 'Newtonsoft.Json.Linq.JArray' does not contains a definition of 'Union'
Newtonsoft.Json now supports merging objects (old link):
var dataObject1 = JObject.Parse(#"{
""data"": [{
""id"": ""id1"",
""field"": ""field1""
}],
""paging"": {
""prev"": ""link1"",
}
}");
var dataObject2 = JObject.Parse(#"{
""data"": [{
""id"": ""id2"",
""field"": ""field2""
}],
""paging"": {
""prev"": ""link2"",
}
}");
var mergeSettings = new JsonMergeSettings
{
MergeArrayHandling = MergeArrayHandling.Union
};
// method 1
(dataObject1.SelectToken("data") as JArray).Merge(dataObject2.SelectToken("data"), mergeSettings);
// method 2
//dataObject1.Merge(dataObject2, mergeSettings);
var mergedArray = dataObject1.SelectToken("data") as JArray;
Console.WriteLine(mergedArray.ToString(Formatting.None));
(checked with brain-compiler ;) )
JArray dataOfJson1=json1.SelectToken("data");
JArray dataofJson2=json2.SelectToken("data");
foreach(JObject innerData in dataofJson2)
{
dataOfJson1.Add(innerData);
}
For those that (like me) cannot use the new JSON.net library.
The following method is what I use.
public static JObject mergeJsonObjects(List<JObject> objects) {
JObject json = new JObject();
foreach(JObject JSONObject in objects) {
foreach(var property in JSONObject) {
string name = property.Key;
JToken value = property.Value;
json.Add(property.Key, property.Value);
}
}
return json;
}
The method takes an list of JObjects and returns a single JObject, simpel and effective.
A possible solution could be:
class Container
{
public List<IdField> data{get;set;}
}
class IdField
{
public string id{get;set;}
public string field{get;set;}
}
string s1 = "{ \"data\": [{ \"id\": \"id1\", \"field\": \"field1\" }], \"paging\": { \"prev\": \"link1\", } }";
string s2 = "{ \"data\": [{ \"id\": \"id2\", \"field\": \"field2\" }], \"paging\": { \"prev\": \"link2\", } }";
var d1 = JsonConvert.DeserializeObject<Container>(s1);
var d2 = JsonConvert.DeserializeObject<Container>(s2);
d1.data.AddRange(d2.data);
var result = JsonConvert.SerializeObject(d1);

Categories