I have a collection with documents containing references to children from same collection.
Collection of Components:
[
{
"_id": "group01",
"type": "Group",
"children": [
{
"_id": "list01",
"order": 0
},
{
"_id": "list02",
"order": 1
}
],
"order": 0
},
{
"_id": "list01",
"type": "List",
"children": [],
"order": 0
},
{
"_id": "list02",
"type": "List",
"children": [
{
"_id": "item01",
"order": 0
}
],
"order": 0
},
{
"_id": "item01",
"type": "Item",
"children": [],
"order": 0
}
]
Model for Component:
[BsonCollection(collectionName: "Components")]
public class Component {
[JsonProperty("_id")]
[BsonElement("_id")]
[BsonId(IdGenerator = typeof (StringObjectIdGenerator))]
public string Id { get; set; }
[JsonProperty("type")]
[BsonElement("type")]
[BsonRequired]
public string Type { get; set; }
[JsonProperty("order")]
[BsonElement("order")]
public int? Order { get; set; }
[JsonProperty("children")]
[BsonElement("children")]
[BsonIgnoreIfNull]
public List <Component> Children { get; set; }
}
While loading the document, I need to load all the nested children for a given id. For example, to get document with _id: group01, I want to fetch all the properties of children nested to all levels like this:
[
{
"_id": "group01",
"type": "Group",
"children": [
{
"_id": "list01",
"type": "List",
"children": [],
"order": 0
},
{
"_id": "list02",
"type": "List",
"children": [
{
"_id": "item01",
"type": "Item",
"children": [],
"order": 0
}
],
"order": 0
}
],
"order": 0
}
]
I am able to get this with recursively populating children but this will take more time when there are more levels in the hierarchy.
public override async Task <Component> GetByIdAsync(Guid id) {
try {
var Components = (MongoCollectionBase<Component>)_dbRepository.GetCollection<Component>();
Component component = await Components.Find(i => i.Id == id.ToString()).FirstOrDefaultAsync();
for (var i = 0; i < component.Children.Count; i++) {
component.Children[i] = await GetByIdAsync(Guid.Parse(component.Children[i].Id));
}
return component;
} catch (Exception ex) {
_logger.LogException(ex, $ "Exception while reading component: {id}", id.ToString());
}
return null;
}
How to achieve this in a more optimized way with aggregation/projection? Please guide me.
In order to load the hierarchy using an aggregate with a $graphLookup, you can use the following pipeline:
var result = await Components.Aggregate()
.Match(Builders<Component>.Filter.Eq(x => x.Id, "group01"))
.GraphLookup<Component, string, string, string, Component, IEnumerable<Component>, Component>(
Components,
"children._id",
"_id",
"$children._id",
"children",
"depth")
.ToListAsync();
The result contains the requested parent with all children and is retrieved from the database in a single request.
For this to work, I adjusted the model:
Removed BsonRequired from Type
Added Depth property
public class Component
{
[JsonProperty("_id")]
[BsonElement("_id")]
public string Id { get; set; }
[JsonProperty("type")]
[BsonElement("type")]
public string Type { get; set; }
[JsonProperty("order")]
[BsonElement("order")]
public int? Order { get; set; }
[JsonProperty("children")]
[BsonElement("children")]
[BsonIgnoreIfNull]
public List<Component> Children { get; set; }
[BsonElement("depth")]
public int Depth { get; set; }
}
Related
{
"name": "Not Okay Bears Solana #1",
"image": "ipfs://QmV7QPwmfc6iFXw2anb9oPZbkFR75zrtw6exd8LherHgvU/1.png",
"attributes": [
{
"trait_type": "Background",
"value": "Amethyst"
},
{
"trait_type": "Fur",
"value": "Warm Ivory"
},
{
"trait_type": "Mouth",
"value": "Clean Smile"
},
{
"trait_type": "Eyes",
"value": "Not Okay"
},
{
"trait_type": "Hat",
"value": "Bucket Hat"
},
{
"trait_type": "Clothes",
"value": "Plaid Jacket"
},
{
"trait_type": "Eyewear",
"value": "Plastic Glasses"
}
],
"description": "Not Okay Bears Solana is an NFT project for mental health awareness. 10k collection on the Polygon blockchain. We are not okay."
}
I need to add an object to attributes.
How to do this?
My JSON classes:
public class Attribute
{
public string trait_type { get; set; }
public string value { get; set; }
}
public class Root
{
public string name { get; set; }
public string image { get; set; }
public List<Attribute> attributes { get; set; }
public string description { get; set; }
}
try this, in this case you don't need any classes
var jsonObject = JObject.Parse(json);
JObject obj = new JObject();
obj.Add("trait_type", "type");
obj.Add("value", "value");
((JArray)jsonObject["attributes"]).Add(obj);
var newJson=jsonObject.ToString();
but if you need the data not a json , you can use this code
Root data = JsonConvert.DeserializeObject<Root>(json);
data.attributes.Add(new Attribute { trait_type="type", value="value"});
I have a model class that allows users to specify the template which is used to create content with specified properties.
public class Model
{
public string Id { get; set; }
public string Alias { get; set; }
public ICollection<Field> Fields { get; set; }
}
public class Field
{
public FieldType Type { get; set; }
public string Alias { get; set; }
}
public enum FieldType
{
String = 1,
Email = 2,
Password = 3,
DateTime = 4,
Integer = 5,
Decimal = 6,
...
}
The content that is created based on this template will have the structure as below:
public class Entry
{
public string _id { get; set; }
public List<FieldValue> FieldValues { get; set; }
}
public class FieldValue
{
public object Value { get; set; }
public string Alias { get; set; }
public FieldType Type { get; set; }
}
So, I for example, can have data such as:
PersonModel
{
"id": "34752b15",
"alias": "person",
"fields": [
{
"type": 1,
"alias": "name"
},
{
"type": 2,
"alias": "email"
}
]
}
PersonEntries:
[
{
"_id": "426e",
"fieldValues": [
{
"alias": "name",
"value": "John",
"type": 1
},
{
"alias": "email",
"value": "john#stark.com",
"type": 2
}
]
},
{
"_id": "c6110fc3cc0e",
"fieldValues": [
{
"alias": "name",
"value": "Arya",
"type": 1
},
{
"alias": "email",
"value": "arya#stark.com",
"type": 2
}
]
}
]
Or:
ShapeModel
{
"id": "afd69af4",
"alias": "shape",
"fields": [
{
"type": 1,
"alias": "name"
},
{
"type": 6,
"alias": "area"
},
{
"type": 6,
"alias": "perimeter"
}
]
}
ShapeEntries:
[
{
"_id": "426e",
"fieldValues": [
{
"alias": "name",
"value": "my-square",
"type": 1
},
{
"alias": "area",
"value": 1,
"type": 6
},
{
"alias": "perimeter",
"value": 4,
"type": 6
}
]
},
{
"_id": "426enbv",
"fieldValues": [
{
"alias": "name",
"value": "my-triangle",
"type": 1
},
{
"alias": "area",
"value": 0.5,
"type": 6
},
{
"alias": "perimeter",
"value": 3,
"type": 6
}
]
}
]
and so on.
The idea is to create graphql schemas dynamically (on runtime) when a new model is created and allow users to query entries based on the model alias.
What I tried is:
public ISchema CreateEntrySchema(List<Entry> entries, Model model, IServiceCollection services)
{
var entryType = new ObjectGraphType() { Name = model.Alias };
entryType.Field("_id", new IdGraphType());
foreach (var field in model.Fields)
{
if (entryType.Fields.FirstOrDefault(f => f.Name == field.Alias) != null)
{
continue;
}
Type clrGraphType = field.Type.GetStandardType().GetGraphTypeFromType(isNullable: true);
IGraphType graphType = (IGraphType)Activator.CreateInstance(clrGraphType);
entryType.Field(field.Alias,
graphType,
resolve: context =>
{
var entry = (Entry)context.Source;
object firstOrDefault = entry?.FieldValues.FirstOrDefault(value => value.Alias == context.FieldDefinition.Name)?.Value;
return firstOrDefault;
});
}
var queryType = new ObjectGraphType();
queryType.Field(
model.Alias,
type: new ListGraphType(entryType),
arguments: new QueryArguments(ConstructArguments(model.Fields)),
resolve: context =>
{
List<KeyValuePair<string, ArgumentValue>> args = context.Arguments?.Where(pair => pair.Value.Value != null).ToList();
if (!args?.Any() ?? true)
{
return entries;
}
return args.Aggregate(entries, (current, pair) =>
current.Where(entry => entry.FieldValues.FirstOrDefault(field => field.Alias == pair.Key) != null && entry.FieldValues.FirstOrDefault(field => field.Alias == pair.Key).Value.Equals(pair.Value.Value))
.ToList());
});
ISchema schema = new Schema { Query = queryType };
schema.RegisterType(entryType);
services.AddGraphQL(a =>
{
a.AddSchema(schema).AddClrTypeMappings();
});
return schema;
}
The problem is that it is registering only the last schema. So, for example, if I will call this method like:
CreateEntrySchema(personEntries, personModel, services);
CreateEntrySchema(shapeEntries, shapeModel, services);
And query like this:
{
shape{
name
}
person{
name
}
}
The result is:
"GraphQL.Validation.Errors.FieldsOnCorrectTypeError: Cannot query field 'person' on type 'Object'. Did you mean 'shape'?",
I need to extract data from mongodb, but this data differs in coordinates, I can create a base-sub class structure and POST it to mongodb, but the coordinates do not come in the GET operation.
public class Geometry
{
public string type { get; set; }
}
public class GeoPoly:Geometry
{
public double[][][] coordinates { get; set; }
}
public class GeoMultipoly : Geometry
{
public double[][][][] coordinates { get; set; }
}
how can I do that
Should the serialization convention change and how should it change
Is the base-sub class structure suitable for this problem?
database entries:
{
"type": "Polygon",
"coordinates": [
[
[
[
51.263632,
60.962938
],
[
30.884274,
20.065517
],
[
68.832044,
14.362602
],
[
51.263632,
60.962938
]
]
]
]
},
{
"type": "MultiPolygon",
"coordinates": [
[
[
[
43.182162,
64.042209
],
[
14.721334,
22.358269
],
[
51.263632,
17.738761
],
[
43.182162,
64.042209
]
]
],
[
[
[
55.831419,
51.446822
],
[
65.66973,
20.065517
],
[
97.64424,
37.509124
],
[
55.831419,
51.446822
]
]
]
]
}
I am not sure is the best idea,
but newtonsoft json.net support serialize and deserialize with $type.
The item will be saved in DB with it's full class identifier.
You can check here
An example:
// {
// "$type": "Newtonsoft.Json.Samples.Stockholder, Newtonsoft.Json.Tests",
// "FullName": "Steve Stockholder",
// "Businesses": {
// "$type": "System.Collections.Generic.List`1[[Newtonsoft.Json.Samples.Business, Newtonsoft.Json.Tests]], mscorlib",
// "$values": [
// {
// "$type": "Newtonsoft.Json.Samples.Hotel, Newtonsoft.Json.Tests",
// "Stars": 4,
// "Name": "Hudson Hotel"
// }
// ]
// }
// }
To use it check newtonsoft documentation here.
Example of usage:
Stockholder stockholder = new Stockholder
{
FullName = "Steve Stockholder",
Businesses = new List<Business>
{
new Hotel
{
Name = "Hudson Hotel",
Stars = 4
}
}
};
string jsonTypeNameAll = JsonConvert.SerializeObject(stockholder, Formatting.Indented, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All
});
For this JSON right class to do is:
public class Rootobject
{
public string type { get; set; }
public float[][][][] coordinates { get; set; }
}
For GeoPoly and GeoMultipoly there practical the same. You can procesig this in two steps:
Get the list od Rootobject (List)
switch(g.type)
{
case "GeoPoly":
result.Add(new GeoPoly
{
type = g.type;
coordinates = g.coordinates
});
break;
case "GeoMultipoly":
result.Add(new GeoMultipoly
{
type = g.type;
coordinates = g.coordinates
});
break;
default:
continue;
}
At the end the result list will have a list of Geometry types in right type.
How to combine JSON objects in the same response that has the same key and value. like if I've two objects that have the same language: Python I want to combine them and List the remaining data under this language Python I don't want it being repeated
[
[
{
"language": "Python",
"id": 319029846,
"full_Name": "beurtschipper/Depix",
"name": "Depix"
},
{
"language": "Python",
"id": 319169382,
"full_Name": "benwilber/boltstream",
"name": "boltstream"
},
{
"language": "Python",
"id": 316899719,
"full_Name": "r0ysue/r0capture",
"name": "r0capture"
}
],
[
{
"language": "YARA",
"id": 318029147,
"full_Name": "fireeye/red_team_tool_countermeasures",
"name": "red_team_tool_countermeasures"
}
],
[
{
"language": "TypeScript",
"id": 313443335,
"full_Name": "pmndrs/valtio",
"name": "valtio"
}
]
]
what the form I want is
[
[
{
"language": "Python",
"id": [319029846, 319169382, 316899719],
"full_Name": ["beurtschipper/Depix", "benwilber/boltstream", "r0ysue/r0capture"],
"name": ["Depix", "boltstream", "r0capture"]
}
],
[
{
"language": "YARA",
"id": 318029147,
"full_Name": "fireeye/red_team_tool_countermeasures",
"name": "red_team_tool_countermeasures"
}
],
[
{
"language": "TypeScript",
"id": 313443335,
"full_Name": "pmndrs/valtio",
"name": "valtio"
}
]
]
And this is the code i'm using
public class Items
{
[JsonPropertyName("language")]
public string Language { get; set; }
[JsonPropertyName("id")]
public int Id { get; set; }
[JsonPropertyName("name")]
public string Name { get; set; }
[JsonPropertyName("full_name")]
public string Full_Name { get; set; }
public string total_count { get; set; }
}
public class Root
{
[JsonPropertyName("items")]
public List<Items> Items { get; set; }
}
Root jObj2 = JsonConvert.DeserializeObject<Root>(readerResult);
var result = jObj2.Items.Select(x => new
{
x.Language,
x.Id,
x.Full_Name,
x.Name
}).GroupBy(x => x.Language).ToArray();
return new JsonResult(result);
GroupBy is a good place to start. Once you have the groups, you need to select the individual properties of each group into a new list:
var result = jObj2.Items
.GroupBy(x => x.Language)
.Select(group => new
{
Language = group.Key,
Ids = group.Select(x => x.Id).ToList(),
FullNames = group.Select(x => x.Full_Name).ToList(),
Names = group.Select(x => x.Name).ToList()
})
.ToArray();
I am trying to deserialize the following JSON, but I really have no idea how to use JSON.net to do the work. I am using C# and JSON.Net library.
My JSON is as follows:
{
"found": 3,
"bounds": [
[
-43.54919,
172.62148
],
[
-43.54487,
172.63654
]
],
"features": [
{
"id": 15342454,
"centroid": {
"type": "POINT",
"coordinates": [
-43.54779,
172.62148
]
},
"bounds": [
[
-43.54779,
172.62148
],
[
-43.54779,
172.62148
]
],
"properties": {
"osm_element": "node",
"amenity": "toilets",
"synthesized_name": "Toilets",
"osm_id": "502884303"
},
"geometry": {
"type": "POINT",
"coordinates": [
-43.54779,
172.62148
]
},
"location": {
"county": "Canterbury",
"country": "New Zealand",
"road": "Sommerset Crescent",
"city": "Christchurch"
},
"type": "Feature"
},
{
"id": 19313858,
"centroid": {
"type": "POINT",
"coordinates": [
-43.54919,
172.63654
]
},
"bounds": [
[
-43.54919,
172.63654
],
[
-43.54919,
172.63654
]
],
"properties": {
"osm_element": "node",
"amenity": "toilets",
"synthesized_name": "Toilets",
"osm_id": "676225633"
},
"geometry": {
"type": "POINT",
"coordinates": [
-43.54919,
172.63654
]
},
"location": {
"county": "Canterbury",
"country": "New Zealand",
"road": "Colombo Street",
"city": "Christchurch"
},
"type": "Feature"
},
{
"id": 22536275,
"centroid": {
"type": "POINT",
"coordinates": [
-43.54487,
172.63632
]
},
"bounds": [
[
-43.54487,
172.63632
],
[
-43.54487,
172.63632
]
],
"properties": {
"osm_element": "node",
"amenity": "toilets",
"synthesized_name": "Toilets",
"osm_id": "864392689"
},
"geometry": {
"type": "POINT",
"coordinates": [
-43.54487,
172.63632
]
},
"location": {
"county": "Canterbury",
"country": "New Zealand",
"road": "Wordsworth Street",
"city": "Christchurch"
},
"type": "Feature"
}
],
"type": "FeatureCollection",
"crs": {
"type": "EPSG",
"properties": {
"code": 4326,
"coordinate_order": [
0,
1
]
}
}
}
You don't have to declare many tiny classes to deserialize. Just make use of dynamic.
Here is a working example
string jsonstr = #"{""found"": 3, ""bounds"": [[-43.54919, 172.62148], [-43.54487, 172.63654]], ""features"": [{""id"": 15342454,""centroid"": {""type"":""POINT"",""coordinates"":[-43.54779, 172.62148]},""bounds"": [[-43.54779, 172.62148], [-43.54779, 172.62148]],""properties"": {""osm_element"": ""node"", ""amenity"": ""toilets"", ""synthesized_name"": ""Toilets"", ""osm_id"": ""502884303""},""geometry"": {""type"":""POINT"",""coordinates"":[-43.54779, 172.62148]},""location"": {""county"": ""Canterbury"", ""country"": ""New Zealand"", ""road"": ""Sommerset Crescent"", ""city"": ""Christchurch""},""type"": ""Feature""},{""id"": 19313858,""centroid"": {""type"":""POINT"",""coordinates"":[-43.54919, 172.63654]},""bounds"": [[-43.54919, 172.63654], [-43.54919, 172.63654]],""properties"": {""osm_element"": ""node"", ""amenity"": ""toilets"", ""synthesized_name"": ""Toilets"", ""osm_id"": ""676225633""},""geometry"": {""type"":""POINT"",""coordinates"":[-43.54919, 172.63654]},""location"": {""county"": ""Canterbury"", ""country"": ""New Zealand"", ""road"": ""Colombo Street"", ""city"": ""Christchurch""},""type"": ""Feature""},{""id"": 22536275,""centroid"": {""type"":""POINT"",""coordinates"":[-43.54487, 172.63632]},""bounds"": [[-43.54487, 172.63632], [-43.54487, 172.63632]],""properties"": {""osm_element"": ""node"", ""amenity"": ""toilets"", ""synthesized_name"": ""Toilets"", ""osm_id"": ""864392689""},""geometry"": {""type"":""POINT"",""coordinates"":[-43.54487, 172.63632]},""location"": {""county"": ""Canterbury"", ""country"": ""New Zealand"", ""road"": ""Wordsworth Street"", ""city"": ""Christchurch""},""type"": ""Feature""}], ""type"": ""FeatureCollection"", ""crs"": {""type"": ""EPSG"", ""properties"": {""code"": 4326, ""coordinate_order"": [0, 1]}}}";
dynamic json = JsonConvert.DeserializeObject(jsonstr);
foreach (var feature in json.features)
{
Console.Write("{0},{1} - {2},{3} : ",
feature.bounds[0][0], feature.bounds[0][1],
feature.bounds[1][0], feature.bounds[1][1]);
Console.WriteLine("{0} {1} {2} {3}",
feature.location.country, feature.location.county, feature.location.city, feature.location.road);
}
Non-dynamic version
JObject json = (JObject)JsonConvert.DeserializeObject(jsonstr);
foreach (var feature in json["features"])
{
Console.Write("{0},{1} - {2},{3} : ",
feature["bounds"][0][0], feature["bounds"][0][1],
feature["bounds"][1][0], feature["bounds"][1][1]);
Console.WriteLine("{0} {1} {2} {3}",
feature["location"]["country"], feature["location"]["county"], feature["location"]["city"], feature["location"]["road"]);
}
public class Centroid
{
public string type { get; set; }
public List<double> coordinates { get; set; }
}
public class Properties
{
public string osm_element { get; set; }
public string amenity { get; set; }
public string synthesized_name { get; set; }
public string osm_id { get; set; }
}
public class Geometry
{
public string type { get; set; }
public List<double> coordinates { get; set; }
}
public class Location
{
public string county { get; set; }
public string country { get; set; }
public string road { get; set; }
public string city { get; set; }
}
public class Feature
{
public int id { get; set; }
public Centroid centroid { get; set; }
public List<List<double>> bounds { get; set; }
public Properties properties { get; set; }
public Geometry geometry { get; set; }
public Location location { get; set; }
public string type { get; set; }
}
public class Properties2
{
public int code { get; set; }
public List<int> coordinate_order { get; set; }
}
public class Crs
{
public string type { get; set; }
public Properties2 properties { get; set; }
}
public class RootObject
{
public int found { get; set; }
public List<List<double>> bounds { get; set; }
public List<Feature> features { get; set; }
public string type { get; set; }
public Crs crs { get; set; }
}
Here you go.
There is a tool for generating C# classes from json http://json2csharp.com/.
First create a class that fits the JSONed object.
Then, simply write JsonConvert.DeserializeObject<ClassName>(json)
Where ClassName is the name of your class and json is a string containing you JSON.
You have quite a complex data structure, so creating a class for it might be a bit complex.
You might want to maybe simplify it a little bit.
How I would tackle this....
First take your JSON and paste it into: http://jsonviewer.stack.hu/ - this gives you a view like:
Now, from the objects shown in that view create a class for each type of object - e.g:
public class MainWrapper
{
public int found {get;set;}
public List<Bound> bounds {get;set;}
public List<Feature> features {get;set;}
public Crs crs {get;set;
}
Finally you can now use some Newtonsoft to deserialize as: JsonConvert.DeserializeObject<MainWrapper>(text)