Find n-level nested SubDocument MongoDB - c#

I have this Document:
{
"$id": "1",
"DocType": "Unidade",
"Nome": "TONY",
"RG_InscricaoEstadual": "4347924938742",
"Setores": [
{
"$id": "9",
"Nome": "Child0",
"Setores": [
{
"$id": "10",
"Nome": "Child1",
"Setores": [
/* <n depth nested level> */
"$id": "11",
"Nome": "Child2",
"Id": "90228c56-eff2-46d2-a324-b04e3c69e15c",
"DocType": "Setor"
],
"Id": "60228c56-dff2-46d2-a324-b04e3c69e15b",
"DocType": "Setor"
}
],
"Id": "8457e1b7-39dc-462c-8f46-871882faea2c",
"DocType": "Setor"
}
]
}
How to query this SubDocument if I want to retrieve a Setor, for example
"Id": "60228c56-dff2-46d2-a324-b04e3c69e15b"
I know that if I know now many levels it is Nested, I can write a query to look for something like
Unidade.Setor.Id=="8457e1b7-39dc-462c-8f46-871882faea2c"
But How can I search for it for a unknown number of nested Levels, for example 1, 2, 3 n levels?
How to find the Setor with Id '90228c56-eff2-46d2-a324-b04e3c69e15c', for example?
Comments about how to solve this question will also be appreciated.

In c# we can create a recursive method to achieve this scenario.
Here is a BsonDocument I created:
BsonDocument doc = new BsonDocument {
{ "id","1"},
{ "DocType", "Unidade"},
{ "Nome", "TONY"},
{ "RG_InscricaoEstadual", "4347924938742"},
{ "Setores",new BsonArray {
new BsonDocument {
{ "id","9" },
{ "Nome", "Child0"},
{ "Setores", new BsonArray { new BsonDocument {
{ "id","10" },
{ "Nome", "Child1"},
{ "Setores", new BsonArray { new BsonDocument {
{ "id","11" },
{ "Nome", "Child2"},
{ "Id","90228c56-eff2-46d2-a324-b04e3c69e15c" },
{ "DocType", "Setor"}
}
}
},
{ "Id","60228c56-dff2-46d2-a324-b04e3c69e15b" },
{ "DocType", "Setor"}
}
}
},
{ "Id","8457e1b7-39dc-462c-8f46-871882faea2c" },
{ "DocType", "Setor"}
}
}
}
};
You can use Mongo c# Query method to get this BsonDocument from MongoDB.
Here is a recursive method I used to query document via "Id":
BsonDocument result = GetID(doc, "90228c56-eff2-46d2-a324-b04e3c69e15c");
public static BsonDocument GetID(BsonDocument doc, string queryId)
{
BsonDocument result = new BsonDocument();
if (doc.Elements.Where(c => c.Name == "Setores").Count() != 0)
{
foreach (var item in doc.GetElement("Setores").Value.AsBsonArray)
{
var id = item.AsBsonDocument.GetElement("Id").Value;
if (id == queryId)
{
result = item.AsBsonDocument;
break;
}
result = GetID(item.AsBsonDocument, queryId);
}
}
return result;
}
I hope this could give you some tips.

The only thing you could do is have nested queries i.e.
find({"Unidade.Setor.Id": ObjectId("8457e1b7-39dc-462c-8f46-871882faea2c")
find({"Unidade.Setor.Setor.Id": ObjectId("8457e1b7-39dc-462c-8f46-871882faea2c")
find({"Unidade.Setor.Setor.Setor.Id": ObjectId("8457e1b7-39dc-462c-8f46-871882faea2c")
Run then one after the other if the previous one fails.
But DON'T!!!
You should be storing these Setor records as separate documents. You can store them with references to each other and then query for them using lookup (like a join in SQL)
https://docs.mongodb.com/manual/reference/operator/aggregation/lookup/

Related

C# Parsing JSON Object

I am wondering if we can take some parts inside a JSON string and print it out. I have a JSON string and I only want to take some specific things in it.
My JSON string:
{
"A": {
"a1":[
{
"a11": {
"a111": "something1",
"a112": "something2"
},
"a12": [
{
"a121": "something1",
"a122": "something2"
},
{
"a211": "something1",
"a212": "soemthing2"
}
]
]
},
"B": {
"b1":[
{
"b11": {
"b111": "something1",
"b112": "something2"
},
"b12": [
{
"b121": "something1",
"b122": "something2"
},
{
"b211": "something1",
"b212": "soemthing2"
}
]
}
]
}
}
I only want to print out: b121 is something1. How can I do it in C#?
Firstly, your JSON example has some problems so I'll give an example using this part as the JSON file.
{
"B": {
"b1":
{
"b11": {
"b111": "something1",
"b112": "something2"
},
"b12": [
{
"b121": "something1",
"b122": "something2"
},
{
"b211": "something1",
"b212": "soemthing2"
}
]
}
}
}
You can access to a specific part like this using Json.NET:
var jsonData = File.ReadAllText(#"\test.json");
dynamic jsonObject = JsonConvert.DeserializeObject(jsonData);
// print out a specific part
Console.WriteLine(jsonObject.B.b1.b11.b111);

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,
}
}
}
}
}
});

Partial copy of dynamic object in c#

As you can see below, in the json i have "data", "data2" and invoice.
My goal is to split a json file in more parts that have "data" and "data2" and a part of "invoice".
I've de-serialized the string, that contain the json code, in a dynamic object so i need to make a partial copy of this in a temp dynamic object, serialize it and than continue my job.
string strJsonOuput = Newtonsoft.Json.JsonConvert.SerializeXmlNode(xmlInput);
dynamic objComune = Newtonsoft.Json.JsonConvert.DeserializeObject<dynamic>(strJsonOuput);
This an example of the json:
{
"data": {
"a": "1",
"b": "2"
},
"data2": {
"something1": {
"thing1": "xxx",
},
"Invoice": [
{
"head": "bbb",
"body": {
"argument1":"aaa"
}
}
{
"head": "xxxx",
"body": {
"argument2": "ccc"
}
}
{
"head": "xxxx",
"body": {
"argument2": "ccc"
}
}
{...}
]
}
}

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": []
}
]
}
]
}
]

There is no argument that corresponds to the required formal parameter

This is probably not the best way to be doing this but it's the best I know how to do with c#. I'm trying to create a dictionary and then convert it to json later. Right now I"m just trying to get the dictionary to match what I want in the json format later. Here is what I have so far:
`Dictionary<Dictionary<string, string>, Dictionary<string, List<List<Decimal>>>> testDict = new Dictionary<Dictionary<string, string>, Dictionary<string, List<List<Decimal>>>>() {
new Dictionary<string, string>() {
{ "test", "test" }
}
};`
This is giving me the following error:
There is no argument that corresponds to the required formal parameter
I don't know what could be causing this and any help would be great, thanks!
Here is the json structure I'm trying to replicate:
[
{
"target": "1",
"datapoints": [
[
67.0,
1491609600.0
]
]
},
{
"target": "2",
"datapoints": [
[
54.0,
1491091200.0
],
[
65.0,
1491177600.0
],
[
69.0,
1491609600.0
],
[
65.0,
1491696000.0
],
[
54.0,
1491868800.0
],
[
63.0,
1491955200.0
],
[
64.0,
1492214400.0
],
[
57.0,
1492732800.0
],
[
72.0,
1492819200.0
],
[
50.0,
1493337600.0
],
[
63.0,
1493424000.0
]
]
},
]
Ok, not the answer to your question, but it will help, is the correct structure for your JSON:
public class TargetClass
{
public string target{ get; set; }
public List<double[]> datapoints{ get; set; }
}
That's the base class. If you want to deserialize what you have in JSON you will do something like this (assuming you are using Newtonsoft Json, else change to the library you use):
var data = Newtonsoft.Json.JsonConvert.DeserializeObject<TargetClass[]>(theString);
And to serialize you would create something like this:
var items = new List<TargetClass>();
var target = new TargetClass{ target = "1", datapoints = new List<double[]>{ new double[]{ 67.0, 1491609600.0 } };
items.Add(target);
var ser = Newtonsoft.Json.JsonConvert.SerializeObject(items);
Using anonymous types you can create complex object hierarchies almost as easy as writing plain JSON:
var obj = new[] {
new {
target = "1",
datapoints = new [] {
new [] {
67.0,
1491609600.0
}
}
},
new {
target = "2",
datapoints = new [] {
new [] {
54.0,
1491091200.0
},
new [] {
65.0,
1491177600.0
},
}
}
};
var json = JsonConvert.SerializeObject(obj, Formatting.Indented);
Demo: https://dotnetfiddle.net/jk8gho
If subcollections in this object are a subject to expansion, these collections should better be explicitly defined as List<dynamic>. Strictly speaking this dynamic is not necessary and explicit types may be used instead, but using dynamic simplifies the definition.
var obj = new List<dynamic> {
new {
target = "1",
datapoints = new List<dynamic> {
new [] {
67.0,
1491609600.0
}
}
},
new {
target = "2",
datapoints = new List<dynamic> {
new [] {
54.0,
1491091200.0
},
new [] {
65.0,
1491177600.0
},
}
}
};
var target2 = obj.Where(t => t.target == "2").Single();
target2.datapoints.Add(new [] {
64.0,
1492214400.0
});
target2.datapoints.Add(new[] {
57.0,
1492732800.0
});
var target3 = new {
target = "3",
datapoints = new List<dynamic> { }
};
target3.datapoints.Add(new[] {
72.0,
1492819200.0
});
obj.Add(target3);
var json = JsonConvert.SerializeObject(obj, Formatting.Indented);
Demo: https://dotnetfiddle.net/d4ZUH8

Categories