I have this Json string :
"UserProperties": [
{
"Id": "3", "values": [
{ "prop": "1" }
]
},
{
"Id": "4", "values": [
{ "prop": "1" },
{ "prop": "2" },
{ "prop": "3" }
]
}
]
I Want to convert this into some sort of c# string and list value like this:
public list<int> Id { get; set; }
public list<object> values { get; set; }
public int prop { get; set; }
So that i can manipulate my values :
foreach( int i in Id)
{
foreach( object val in values)
{
var str = i + '-' + val.prop;
}
}
So far what i have done is create a class that will contain those Json string. I get this code from an quite similar approach.
Create a wrapper class
class Wrapper {
[JsonProperty("UserPropertiees")]
public ValueSet valueSet { get; set; }
}
class ValueSet{
[JsonProperty("values")]
public List<string> values {get;set;}
public PropertySet propertySet { get; set; }
}
class PropertySet{
[JsonProperty("property")]
public List<string> property {get;set;}
}
I would use Newtonsoft's Json.NET and deserialize your JSON array into your List.
http://www.newtonsoft.com/json/help/html/SerializingCollections.htm
As per Rob Stewart's advice, NewtonSoft.JSON is the best way to go IMO. Here's something you can put in a console app to have a play with:
string json = #"{""UserProperties"": [
{
""Id"": ""3"", ""values"": [
{ ""prop1"": ""1"" }
]
},
{
""Id"": ""4"", ""values"": [
{ ""prop1"": ""1"" },
{ ""prop2"": ""2"" },
{ ""prop3"": ""3"" }
]
}
]}";
dynamic obj = JObject.Parse(json);
foreach (var o in obj.UserProperties)
{
Console.WriteLine(o.Id);
}
Console.ReadLine();
EDIT
As per your comments, here is a more complete example. Try this:
dynamic obj = JObject.Parse(json);
foreach (var o in obj.UserProperties)
{
var sb = new StringBuilder();
sb.Append(o.Id);
sb.Append(":");
bool hasProps = false;
foreach (var value in o.values)
{
if (value.prop1 != null)
{
sb.Append(value.prop1);
sb.Append(',');
hasProps = true;
}
if (value.prop2 != null)
{
sb.Append(value.prop2);
sb.Append(',');
hasProps = true;
}
if (value.prop3 != null)
{
sb.Append(value.prop3);
sb.Append(',');
hasProps = true;
}
}
if (hasProps)
{
sb.Remove(sb.Length - 1, 1); // Remove trailing comma
}
Console.WriteLine(sb.ToString());
}
Console.ReadLine();
Related
I am comparing two json objects using JsonDiffPatch.Diff() method and I get the below JSON Structure:
{
"complexity": {
"_t": "a",
"0": {
"phases": {
"_t": "a",
"0": {
"activities": {
"_t": "a",
"_0": [
{
"name": "NAME",
"sortId": 15,
"isCritical": true,
"url": "https://google.com",
"processGroupName": "pgm",
"isDeliverable": "no"
},
0,
0
]
}
}
}
},
"3": {
"phases": {
"_t": "a",
"0": {
"sortId": [
55,
0,
0
]
},
"1": {
"activities": {
"_t": "a",
"_0": [
{
"name": "SName",
"sortId": 12,
"isCritical": false,
"url": "https://google.com",
"processGroupName": "pgm",
"isDeliverable": "Yes"
},
0,
0
]
}
}
}
}
}
}
The result that I want is an object containing List of Phases which contains List of Activities. As per the above JSON I want:
0[Phases and Related Activities]
3[Phases and Related Activities]
I have written the below code:
List<JToken> tokens = diffJson.Children().Children().ToList();
foreach (var token in tokens)
{
//Console.WriteLine(token.ToJson());
var phases = token["0"].Children().Children();
Console.WriteLine(phases.Count());
var activities = phases["0"].Children().Children();
Console.WriteLine();
}
But this will only be for the first set which is for "complexity"["0"]. But how to get the data into an object which contains List.
I have tried the below code also:
JavaScriptSerializer serializer = new JavaScriptSerializer();
dynamic item = serializer.Deserialize<object>(output);
Could anybody help me out in understanding how to Deserialize this complex JSON into simple objects?
I am thinking I will ignore "_t":"a" object under phases and start reading activities(again ignoring "_t": "a" object). Let me see how successful I will be.
Expected Output will be creating objects of below classes:
using System;
using System.Generic;
public class Difference {
public List<Phase> Phases { get; set; }
}
public class Phase {
//For other properties like sortID
public List<Dictionary<string, string>> Properties { get; set; }
public List<Activity> Activites { get; set; }
}
public class Activity {
public string name { get; set; }
public int sortId { get; set; }
public bool isCritical { get; set; }
public string url { get; set; }
public string processGroupName { get; set; }
public string isDeliverable { get; set; }
}
Below is the code which partially works:
JavaScriptSerializer serializer = new JavaScriptSerializer();
dynamic item = serializer.Deserialize<object>(output);
foreach (KeyValuePair<string, dynamic> complexity in item["complexity"])
{
if (complexity.Key != "_t")
{
foreach (KeyValuePair<string, dynamic> phases in complexity.Value["phases"])
{
if (phases.Key != "_t")
foreach (KeyValuePair<string, dynamic> activities in phases.Value["activities"])
{
Console.WriteLine(activities.Value);
}
}
}
}
But this loop breaks when simple key value like sortId comes into picture.
Thanks in Advance.
As per what you have mentioned in the comment on #MarcoSalemo's answer i have modified the Sample Json to suit your requirement the Json now consists of 3 sub documents and 2 with 1 activity each sub-document except for the last one which has 2 activities and a properties object as sortId :
Sample JSON:
var jsonString = #"{
'complexity': {
'_t': 'a',
'0': {
'phases': {
'_t': 'a',
'0': {
'activities': {
'_t': 'a',
'_0': [
{
'name': 'NAME',
'sortId': 15,
'isCritical': true,
'url': 'https://google.com',
'processGroupName': 'pgm',
'isDeliverable': 'no'
},
0,
0
]
}
}
}
},
'1': {
'phases': {
'_t': 'a',
'0': {
'activities': {
'_t': 'a',
'_0': [
{
'name': 'NAME1',
'sortId': 155,
'isCritical': true,
'url': 'https://google.com',
'processGroupName': 'pgm',
'isDeliverable': 'no'
},
0,
0
]
}
}
}
},
'3': {
'phases': {
'_t': 'a',
'0': {
'sortId': [
55,
0,
0
]
},
'1': {
'activities': {
'_t': 'a',
'_0': [
{
'name': 'SName',
'sortId': 12,
'isCritical': false,
'url': 'https://google.com',
'processGroupName': 'pgm',
'isDeliverable': 'Yes'
},
0,
0
],
'_1': [
{
'name': 'SName1',
'sortId': 123,
'isCritical': false,
'url': 'https://google.com',
'processGroupName': 'pgm',
'isDeliverable': 'Yes'
},
0,
0
]
}
}
}
}
}
}";
And the deserialization is achieved as:
var jobj = JObject.Parse(jsonString);
List<Phase> Phases = new List<Phase>();
var complexity = jobj.SelectToken("complexity");
var childrens = complexity.Children().ToList();
for (int i = 1; i < childrens.Count; i++)
{
var activities = new List<Activity>();
var _properties = new List<Dictionary<string, string>>();
var child = childrens[i].Children();//getting all the sub-documets in the json
foreach (var subChild in child)
{
var phases = subChild.SelectToken("phases").Children().ToList();//getting JTokens having key "phases"
for (int j = 1; j < phases.Count; j++)
{
var phaseResult = phases[j].Children().ToList(); //getting all the children of the //phases JToken and itterating over them
foreach (var item in phaseResult)
{
if (item["activities"] != null) //producing the "Activity(s)" object
{
var acts = item.SelectToken("activities").Children().ToList();
for (int k = 1; k < acts.Count; k++)
{
var act = acts[k].Children().ToList();
foreach (var entity in act)
{
var jarvalue = JArray.Parse(entity.ToString()).Children().ToArray()[0].ToString();
var objAct = JsonConvert.DeserializeObject<Activity>(jarvalue);
activities.Add(objAct);
}
}
}
else
{
//if not Activity object than producing Properties object
var _props = item.Children<JToken>().ToList();
var nProeprties = new Dictionary<string, string>();
foreach (var content in _props)
{
var _contentProp = ((Newtonsoft.Json.Linq.JProperty)content); //converting the property object of JtokenType to JProperty to get the Name and JValue
nProeprties.Add(_contentProp.Name, _contentProp.Value.ToString());
}
_properties.Add(nProeprties);
}
}
}
}
Phases.Add(new Phase { Activites = activities, Properties = _properties });//appending the extracted output to the mail object "Phases"
}
As per the above sample json, the serialized output would be something like this:
JsonConvert.SerializeObject(Phases);
[
{
"Properties":[
],
"Activites":[
{
"name":"NAME",
"sortId":15,
"isCritical":true,
"url":"https://google.com",
"processGroupName":"pgm",
"isDeliverable":"no"
}
]
},
{
"Properties":[
],
"Activites":[
{
"name":"NAME1",
"sortId":155,
"isCritical":true,
"url":"https://google.com",
"processGroupName":"pgm",
"isDeliverable":"no"
}
]
},
{
"Properties":[
{
"sortId":"[\r\n 55,\r\n 0,\r\n 0\r\n]"
}
],
"Activites":[
{
"name":"SName",
"sortId":12,
"isCritical":false,
"url":"https://google.com",
"processGroupName":"pgm",
"isDeliverable":"Yes"
},
{
"name":"SName1",
"sortId":123,
"isCritical":false,
"url":"https://google.com",
"processGroupName":"pgm",
"isDeliverable":"Yes"
}
]
}
]
Note:
I am using Newtonsoft.Json to serialize/deserialize the json.
This is just a simple approach there can be many(Even better) to achieve what you want. So, please test it out with your data and let me know if it needs a modification.
Use dynamic and deserialize the json with JSON.NET
I have stored JSON data in a string and by using the JObject, I am trying to get values from JSON data. I am just not able to figure out that what is the underlying issue with my code because I am not able to get data from the JSON object. A snippet of my code is attached below. If some can help me out to figure out the issue it will be immensely appreciated.
String text;
try {
var response = (HttpWebResponse)request.GetResponse();
using (var sr = new StreamReader(response.GetResponseStream()))
{
text = sr.ReadToEnd();
JObject jObject = JObject.Parse(text);
string haha = (string)jObject["value/segments/requests/count/sum"];
ViewBag.gotstring = haha;
}
System.Diagnostics.Debug.WriteLine(text);
} catch(Exception e) {
System.Diagnostics.Debug.WriteLine(url);
System.Diagnostics.Debug.WriteLine(e.ToString()); }
return View();
Here is the JSON:
{
"value": {
"start": "2018-08-12T04:44:38.941Z",
"end": "2018-08-12T16:44:38.941Z",
"interval": "PT30M",
"segments": [
{
"start": "2018-08-12T14:00:00Z",
"end": "2018-08-12T14:30:00Z",
"segments": [
{
"requests/count": {
"sum": 2
},
"request/name": "GET Home/Index"
},
{
"requests/count": {
"sum": 1
},
"request/name": "GET Home/About"
},
{
"requests/count": {
"sum": 1
},
"request/name": "GET Home/Contact"
}
]
},
{
"start": "2018-08-12T14:30:00Z",
"end": "2018-08-12T15:00:00Z",
"segments": [
{
"requests/count": {
"sum": 2
},
"request/name": "GET Account/Register"
},
{
"requests/count": {
"sum": 1
},
"request/name": "GET Account/Login"
}
]
},
{
"start": "2018-08-12T15:30:00Z",
"end": "2018-08-12T16:00:00Z",
"segments": [
{
"requests/count": {
"sum": 8
},
"request/name": "GET Home/Index"
},
{
"requests/count": {
"sum": 8
},
"request/name": "GET Home/About"
}
]
}
]
}
}
jObject does not work this way. It returns dictionary that you can query by key, but keys are single level. I.e. you'll be able to get some data like this:
var haha = jObject["value"]["segments"];
But beyond that it gets very complex. You'll be much better off defining a C# class that represents your JSON and serialise into that. A simple `Edit=>Paste Special => JSON as Class" in Visual Studio gives this:
public class Rootobject
{
public Value value { get; set; }
}
public class Value
{
public DateTime start { get; set; }
public DateTime end { get; set; }
public string interval { get; set; }
public Segment[] segments { get; set; }
}
public class Segment
{
public DateTime start { get; set; }
public DateTime end { get; set; }
public Segment1[] segments { get; set; }
}
public class Segment1
{
[JsonProperty("requests/count")]
public RequestsCount requestscount { get; set; }
[JsonProperty("request/name")]
public string requestname { get; set; }
}
public class RequestsCount
{
public int sum { get; set; }
}
and then deserialise like this:
var serialised = JsonConvert.DeserializeObject<Rootobject>(json);
var haha = serialised.value.segments.FirstOrDefault().segments.FirstOrDefault().requestscount.sum;
And here is a working sample: https://dotnetfiddle.net/CZgMNE
Can you try:
EDIT: seems like segments is an array, this will get you the sum for first segment only
string haha = (string)jObject["value"]["segments"][0]["segments"]["requests/count"]["sum"];
How can I make Json.NET serializer to serialize instance into array of objects?
Basically I need something like this:
[
{
"name":"Page1.html",
"size":1,
"outlinks":[
"Page2.html",
"Page3.html",
"Page4.html"
]
},
{
"name":"Page2.html",
"size":2,
"outlinks":[
"Page3.html"
]
},
{
"name":"Page3.html",
"size":3,
"outlinks":[
"Page1.html",
"Page2.html",
"Page3.html",
"Page4.html"
]
},
{
"name":"Page4.html",
"size":4,
"outlinks":[]
}
]
with:
Dictionary<string, string[]> UrlsCollection = new Dictionary<string, string[]>();
List<string> OutLinks = new List<string>();
OutLinks.Add("Page2.html");
OutLinks.Add("Page3.html");
OutLinks.Add("Page4.html");
UrlsCollection.Add("Page1.html", OutLinks.ToArray());
OutLinks.Clear();
OutLinks.Add("Page3.html");
UrlsCollection.Add("Page2.html", OutLinks.ToArray());
OutLinks.Clear();
OutLinks.Add("Page1.html");
OutLinks.Add("Page2.html");
OutLinks.Add("Page3.html");
OutLinks.Add("Page4.html");
UrlsCollection.Add("Page3.html", OutLinks.ToArray());
OutLinks.Clear();
UrlsCollection.Add("Page4.html", OutLinks.ToArray());
OutLinks.Clear();
string jsonUrlsCollection = JsonConvert.SerializeObject(UrlsCollection.ToList(), Formatting.Indented);
I get:
[
{
"Key": "Page1.html",
"Value": [
"Page2.html",
"Page3.html",
"Page4.html"
]
},
{
"Key": "Page2.html",
"Value": [
"Page3.html"
]
},
{
"Key": "Page3.html",
"Value": [
"Page1.html",
"Page2.html",
"Page3.html",
"Page4.html"
]
},
{
"Key": "Page4.html",
"Value": []
}
]
There must be a way/something to get a simple JSON Object?
Modifying the solution to reflect Deblaton Jean-Philippe's suggestion in the comments.
public class UrlDef
{
public UrlDef() { outlinks = new List<string>(); }
public string name { get; set; }
public int size { get; set; }
public List<string> outlinks { get; set; }
}
List<UrlDef> UrlsCollection = new List<UrlDef>();
UrlDef urldef;
urldef = new UrlDef();
urldef.name = "Page1.html";
urldef.size = 1;
urldef.outlinks.Add("Page2.html");
urldef.outlinks.Add("Page3.html");
urldef.outlinks.Add("Page4.html");
UrlsCollection.Add(urldef);
urldef = new UrlDef();
urldef.name = "Page2.html";
urldef.size = 2;
urldef.outlinks.Add("Page3.html");
UrlsCollection.Add(urldef);
urldef = new UrlDef();
urldef.name = "Page3.html";
urldef.size = 3;
urldef.outlinks.Add("Page1.html");
urldef.outlinks.Add("Page2.html");
urldef.outlinks.Add("Page3.html");
urldef.outlinks.Add("Page4.html");
UrlsCollection.Add(urldef);
urldef = new UrlDef();
urldef.name = "Page4.html";
urldef.size = 4;
UrlsCollection.Add(urldef);
string jsonUrlsCollection = JsonConvert.SerializeObject(UrlsCollection, Formatting.Indented);
You need to serialize a list of this object
public class Url
{
public string name { get; set; }
public int size { get; set; }
public List<string> outlinks { get; set; }
}
For this answer, I used a website I often use when I know what I expect from JS, but I'm not sure of what I need to feed into the CS : http://json2csharp.com/
I have doubly linked list that I am trying to deserialise.
My scenario closely relates to this SO: Doubly Linked List to JSON
I have the following JSON settings:
_jsonSettings = new JsonSerializerSettings()
{
TypeNameHandling = TypeNameHandling.Auto,
ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor,
PreserveReferencesHandling = PreserveReferencesHandling.Objects,
ObjectCreationHandling = ObjectCreationHandling.Auto
};
When I look at the serialised output, it appears correct, and the references between nodes are properly represented.
When the data is deserialised, the Parent properties in the Child objects are null, even though they are populated with $ref correctly.
Below is a sample of the JSON (trimmed for readability)
In the process of typing this question - I may have seen the source of the trouble...
The objects in the "Children" array property do not have $type attributes.
This could be because the Children and Parent properties are of generic type T.
Note that the actual type being serialised is a derived class of TemplateDataLinkedListBase
public class TemplateDataQueryElement : TemplateDataLinkedListBase<TemplateDataQueryElement>
Here is an excerpt of the base class:
public class TemplateDataLinkedListBase<T> where T : TemplateDataLinkedListBase<T>
{
[JsonProperty(TypeNameHandling = TypeNameHandling.Objects)]
public T Parent { get; set; }
[JsonProperty(TypeNameHandling=TypeNameHandling.Objects)]
public List<T> Children { get; set; }
}
How can I deserialise this JSON in such a way that the Parent property is not null and contains a reference to the parent object?
{
"$id": "9",
"$type": "Contracts.Models.TemplateDataQueryElement, Contracts",
"Query": null,
"Parent": null,
"Children": [
{
"$id": "11",
"Query": null,
"Parent": {
"$ref": "9"
},
"Children": [
{
"$id": "13",
"Query": null,
"Parent": {
"$ref": "11"
},
"Children": [],
"EntityName": "Widgets",
"Fields": [
"Id"
],
"Key": ""
},
Here are PasteBin links to the relevant code:
http://pastebin.com/i1jxVGG3
http://pastebin.com/T1xqEWW2
http://pastebin.com/ha42SeF7
http://pastebin.com/cezwZqx6
http://pastebin.com/uFbTbUZe
http://pastebin.com/sRhNQgzh
Here is what I tried and worked fine:
The classes
public class TemplateDataLinkedListBase<T> where T : TemplateDataLinkedListBase<T>
{
[JsonProperty(TypeNameHandling = TypeNameHandling.Objects)]
public T Parent { get; set; }
[JsonProperty(TypeNameHandling = TypeNameHandling.Objects)]
public List<T> Children { get; set; }
}
public class TemplateDataQueryElement : TemplateDataLinkedListBase<TemplateDataQueryElement>
{
public string Query { get; set; }
public TemplateDataQueryElement()
{
Children = new List<TemplateDataQueryElement>();
}
}
Initialization
var childLowest = new TemplateDataQueryElement
{
Query = "Lowest"
};
var childMiddle = new TemplateDataQueryElement
{
Query = "Middle",
Children = new List<TemplateDataQueryElement>
{
childLowest
}
};
childLowest.Parent = childMiddle;
var parent = new TemplateDataQueryElement
{
Query = "Parent",
Children = new List<TemplateDataQueryElement>
{
childMiddle
}
};
childMiddle.Parent = parent;
Serialization settings
var _jsonSettings = new JsonSerializerSettings()
{
TypeNameHandling = TypeNameHandling.Auto,
ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor,
PreserveReferencesHandling = PreserveReferencesHandling.Objects,
ObjectCreationHandling = ObjectCreationHandling.Auto
};
Serialization
var serializedStr = JsonConvert.SerializeObject(parent, Formatting.Indented, _jsonSettings);
The serialized json looks like this:
{
"$id": "1",
"Query": "Parent",
"Parent": null,
"Children": [
{
"$id": "2",
"Query": "Middle",
"Parent": {
"$ref": "1"
},
"Children": [
{
"$id": "3",
"Query": "Lowest",
"Parent": {
"$ref": "2"
},
"Children": []
}
]
}
]
}
Deserialization
var deserializedStructure = JsonConvert.DeserializeObject<TemplateDataQueryElement>(serializedStr, _jsonSettings);
The references in the deserializedStructure are preserved correctly.
Demo
https://dotnetfiddle.net/j1Qhu6
UPDATE 1
The reason my example works, and the code you posted in the additional links doesn't is because my classes contain default constructor, and yours don't. Analyzing your classes, adding a default constructor to them, it won't break the functionality and the deserialization will be successful with Parent property initialized correctly. So what you basically need to do is add a default constructor to both classes:
public class TemplateDataLinkedListBase<T> where T : TemplateDataLinkedListBase<T>
{
[JsonProperty(TypeNameHandling = TypeNameHandling.Objects)]
public T Parent { get; set; }
[JsonProperty(TypeNameHandling=TypeNameHandling.Objects)]
public List<T> Children { get; set; }
public string EntityName { get; set; }
public HashSet<string> Fields { get; set; }
public string Key { get { return getKey(); } }
public TemplateDataLinkedListBase()
{
Children = new List<T>();
Fields = new HashSet<string>();
}
public TemplateDataLinkedListBase(string entityName)
{
EntityName = entityName;
Children = new List<T>();
Fields = new HashSet<string>();
}
private string getKey()
{
List<string> keys = new List<string>();
keys.Add(this.EntityName);
getParentKeys(ref keys, this);
keys.Reverse();
return string.Join(".", keys);
}
private void getParentKeys(ref List<string> keys, TemplateDataLinkedListBase<T> element)
{
if (element.Parent != null)
{
keys.Add(element.Parent.EntityName);
getParentKeys(ref keys, element.Parent);
}
}
public T AddChild(T child)
{
child.Parent = (T)this;
Children.Add(child);
return (T)this;
}
public T AddChildren(List<T> children)
{
foreach (var child in children)
{
child.Parent = (T)this;
}
Children.AddRange(children);
return (T)this;
}
public void AddFields(IEnumerable<string> fields)
{
foreach (var field in fields)
this.Fields.Add(field);
}
public TemplateDataLinkedListBase<T> Find(string searchkey)
{
if (this.Key == searchkey)
{
return this;
}
else
{
foreach (var child in Children)
{
if (child.Key == searchkey)
{
return child;
}
else
{
var childResult = child.Find(searchkey);
if (childResult != null) return childResult;
}
}
}
return null;
}
}
public class TemplateDataQueryElement : TemplateDataLinkedListBase<TemplateDataQueryElement>, ITemplateDataQueryElement
{
public string TemplateModelName { get; set; }
public string RecordId { get; set; }
public string ParentForeignKeyName { get; set; }
public string Query { get; set; }
public dynamic ObjectData { get; set; }
public ITemplateDataParseResult ParseResult { get; set; }
public TemplateDataQueryElement() : base()
{
Fields.Add("Id"); //Always retrieve Id's
ObjectData = new ExpandoObject();
}
public TemplateDataQueryElement(string entityName)
: base(entityName)
{
Fields.Add("Id"); //Always retrieve Id's
ObjectData = new ExpandoObject();
}
public override string ToString()
{
return string.Format("{0}: {1}", EntityName, Query);
}
}
The EntityName property which you set through your constructor, will be deserialized correctly, since it is a public property.
I am using the 10Gen sanctioned c# driver for mongoDB for a c# application and for data browsing I am using Mongovue.
Here are two sample document schemas:
{
"_id": {
"$oid": "4ded270ab29e220de8935c7b"
},
"Relationships": [
{
"RelationshipType": "Person",
"Attributes": {
"FirstName": "Travis",
"LastName": "Stafford"
}
},
{
"RelationshipType": "Student",
"Attributes": {
"GradMonth": "",
"GradYear": "",
"Institution": "Test1",
}
},
{
"RelationshipType": "Staff",
"Attributes": {
"Department": "LIS",
"OfficeNumber": "12",
"Institution": "Test2",
}
}
]
},
{
"_id": {
"$oid": "747ecc1dc1a79abf6f37fe8a"
},
"Relationships": [
{
"RelationshipType": "Person",
"Attributes": {
"FirstName": "John",
"LastName": "Doe"
}
},
{
"RelationshipType": "Staff",
"Attributes": {
"Department": "Dining",
"OfficeNumber": "1",
"Institution": "Test2",
}
}
]
}
I need a query that ensures that both $elemMatch criteria are met so that I can match the first document, but not the second. The following query works in Mongovue.
{
'Relationships': { $all: [
{$elemMatch: {'RelationshipType':'Student', 'Attributes.Institution': 'Test1'}},
{$elemMatch: {'RelationshipType':'Staff', 'Attributes.Institution': 'Test2'}}
]}
}
How can I do the same query in my c# code?
There is no way to build above query using c# driver (at least in version 1.0).
But you can build another, more clear query, that will return same result:
{ "Relationships" :
{ "$elemMatch" :
{ "RelationshipType" : "Test",
"Attributes.Institution" : { "$all" : ["Location1", "Location2"] }
}
}
}
And the same query from c#:
Query.ElemMatch("Relationships",
Query.And(
Query.EQ("RelationshipType", "Test"),
Query.All("Attributes.Institution", "Location1", "Location2")));
A simple solution is to string together multiple IMongoQuery(s) and then join them with a Query.And at the end:
List<IMongoQuery> build = new List<IMongoQuery>();
build.Add(Query.ElemMatch("Relationships", Query.EQ("RelationshipType", "Person")));
var searchQuery = String.Format("/.*{0}.*/", "sta");
build.Add(Query.ElemMatch("Relationships", Query.Or(Query.EQ("Attributes.FirstName", new BsonRegularExpression(searchQuery)), Query.EQ("Attributes.LastName", new BsonRegularExpression(searchQuery)))));
var _main = Query.And(build.ToArray());
var DB = MongoDatabase.Create("UrlToMongoDB");
DB.GetCollection<ObjectToQuery>("nameOfCollectionInMongoDB").FindAs<ObjectToQuery>(_main).ToList();
`
I have solved the immediate issue by contruction a set of class that allowed for the generation of the following query:
{ 'Relationships':
{
$all: [
{$elemMatch: {'RelationshipType':'Student', 'Attributes.Institution': 'Test1'}},
{$elemMatch: {'RelationshipType':'Staff', 'Attributes.Institution': 'Test2'}}
]
}
}
Here are the class definitions:
class MongoQueryAll
{
public string Name { get; set; }
public List<MongoQueryElement> QueryElements { get; set; }
public MongoQueryAll(string name)
{
Name = name;
QueryElements = new List<MongoQueryElement>();
}
public override string ToString()
{
string qelems = "";
foreach (var qe in QueryElements)
qelems = qelems + qe + ",";
string query = String.Format(#"{{ ""{0}"" : {{ $all : [ {1} ] }} }}", this.Name, qelems);
return query;
}
}
class MongoQueryElement
{
public List<MongoQueryPredicate> QueryPredicates { get; set; }
public MongoQueryElement()
{
QueryPredicates = new List<MongoQueryPredicate>();
}
public override string ToString()
{
string predicates = "";
foreach (var qp in QueryPredicates)
{
predicates = predicates + qp.ToString() + ",";
}
return String.Format(#"{{ ""$elemMatch"" : {{ {0} }} }}", predicates);
}
}
class MongoQueryPredicate
{
public string Name { get; set; }
public object Value { get; set; }
public MongoQueryPredicate(string name, object value)
{
Name = name;
Value = value;
}
public override string ToString()
{
if (this.Value is int)
return String.Format(#" ""{0}"" : {1} ", this.Name, this.Value);
return String.Format(#" ""{0}"" : ""{1}"" ", this.Name, this.Value);
}
}
Helper Search Class:
public class IdentityAttributeSearch
{
public string Name { get; set; }
public object Datum { get; set; }
public string RelationshipType { get; set; }
}
Example Usage:
public List<IIdentity> FindIdentities(List<IdentityAttributeSearch> searchAttributes)
{
var server = MongoServer.Create("mongodb://localhost/");
var db = server.GetDatabase("IdentityManager");
var collection = db.GetCollection<MongoIdentity>("Identities");
MongoQueryAll qAll = new MongoQueryAll("Relationships");
foreach (var search in searchAttributes)
{
MongoQueryElement qE = new MongoQueryElement();
qE.QueryPredicates.Add(new MongoQueryPredicate("RelationshipType", search.RelationshipType));
qE.QueryPredicates.Add(new MongoQueryPredicate("Attributes." + search.Name, search.Datum));
qAll.QueryElements.Add(qE);
}
BsonDocument doc = MongoDB.Bson.Serialization
.BsonSerializer.Deserialize<BsonDocument>(qAll.ToString());
var identities = collection.Find(new QueryComplete(doc)).ToList();
return identities;
}
I am sure there is a much better way but this worked for now and appears to be flexible enough for my needs. All suggestions are welcome.
This is probably a separate question but for some reason this search can take up to 24 seconds on a document set of 100,000. I have tried adding various indexes but to no avail; any pointers in this regards would be fabulous.